httparse-1.8.0/.cargo_vcs_info.json0000644000000001360000000000100126740ustar { "git": { "sha1": "638848eee31f6eeb604dc3e4358941c8905e65eb" }, "path_in_vcs": "" }httparse-1.8.0/.github/FUNDING.yml000064400000000000000000000000261046102023000146370ustar 00000000000000github: [seanmonstar] httparse-1.8.0/.github/workflows/ci.yml000064400000000000000000000057411046102023000162060ustar 00000000000000name: CI on: pull_request: push: branches: - master env: RUST_BACKTRACE: 1 jobs: ci-pass: name: CI is green runs-on: ubuntu-latest needs: - test - simd - msrv - miri steps: - run: exit 0 test: name: Test ${{ matrix.rust }} on ${{ matrix.os }} strategy: matrix: rust: - stable - beta - nightly os: - ubuntu-latest - windows-latest - macOS-latest include: - rust: nightly benches: true runs-on: ${{ matrix.os }} env: CARGO_CFG_HTTPARSE_DISABLE_SIMD: 1 steps: - name: Checkout uses: actions/checkout@v1 - name: Install Rust (${{ matrix.rust }}) uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: ${{ matrix.rust }} override: true - name: no_std uses: actions-rs/cargo@v1 with: command: build args: --no-default-features - name: Test uses: actions-rs/cargo@v1 with: command: test - name: Test all benches if: matrix.benches uses: actions-rs/cargo@v1 with: command: test args: --benches simd: name: SIMD ${{ matrix.target_feature }} on ${{ matrix.rust }} runs-on: ubuntu-latest strategy: matrix: rust: - stable - beta - nightly target_feature: - "+sse4.2" - "+avx2" - "+sse4.2,+avx2" disable_compiletime: - 0 - 1 steps: - name: Checkout uses: actions/checkout@v1 - name: Install Rust (${{ matrix.rust }}) uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: ${{ matrix.rust }} override: true - name: Test uses: actions-rs/cargo@v1 with: command: test env: RUSTFLAGS: -C target_feature=${{ matrix.target_feature }} CARGO_CFG_HTTPARSE_DISABLE_SIMD_COMPILETIME: ${{ matrix.disable_compiletime }} msrv: name: msrv runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v1 - name: Install Rust uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: 1.36.0 override: true # Only build, dev-dependencies don't compile on 1.10 - name: Build uses: actions-rs/cargo@v1 with: command: build miri: name: Test with Miri runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v1 - name: Install Rust uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: nightly components: miri override: true - name: Test run: MIRIFLAGS="-Zmiri-tag-raw-pointers -Zmiri-check-number-validity" cargo miri test httparse-1.8.0/.gitignore000064400000000000000000000000221046102023000134460ustar 00000000000000target Cargo.lock httparse-1.8.0/Cargo.toml0000644000000022430000000000100106730ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "httparse" version = "1.8.0" authors = ["Sean McArthur "] build = "build.rs" description = "A tiny, safe, speedy, zero-copy HTTP/1.x parser." documentation = "https://docs.rs/httparse" readme = "README.md" keywords = [ "http", "parser", "no_std", ] categories = [ "network-programming", "no-std", "parser-implementations", "web-programming", ] license = "MIT/Apache-2.0" repository = "https://github.com/seanmonstar/httparse" [profile.bench] opt-level = 3 lto = true codegen-units = 1 [lib] bench = false [[bench]] name = "parse" harness = false [dev-dependencies.criterion] version = "0.3.5" [features] default = ["std"] std = [] httparse-1.8.0/Cargo.toml.orig000064400000000000000000000012471046102023000143570ustar 00000000000000[package] name = "httparse" version = "1.8.0" authors = ["Sean McArthur "] license = "MIT/Apache-2.0" description = "A tiny, safe, speedy, zero-copy HTTP/1.x parser." repository = "https://github.com/seanmonstar/httparse" documentation = "https://docs.rs/httparse" readme = "README.md" keywords = ["http", "parser", "no_std"] categories = ["network-programming", "no-std", "parser-implementations", "web-programming"] edition = "2018" build = "build.rs" [features] default = ["std"] std = [] [dev-dependencies] criterion = "0.3.5" [lib] bench = false [[bench]] name = "parse" harness = false [profile.bench] lto = true codegen-units = 1 opt-level = 3 httparse-1.8.0/LICENSE-APACHE000064400000000000000000000251371046102023000134200ustar 00000000000000 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. httparse-1.8.0/LICENSE-MIT000064400000000000000000000020471046102023000131230ustar 00000000000000Copyright (c) 2015-2021 Sean McArthur Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 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. httparse-1.8.0/README.md000064400000000000000000000031731046102023000127470ustar 00000000000000# httparse [![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] 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 let mut headers = [httparse::EMPTY_HEADER; 64]; let mut req = httparse::Request::new(&mut headers); let buf = b"GET /index.html HTTP/1.1\r\nHost"; assert!(req.parse(buf)?.is_partial()); // a partial request, so we try again once we have more data let buf = b"GET /index.html HTTP/1.1\r\nHost: example.domain\r\n\r\n"; assert!(req.parse(buf)?.is_complete()); ``` ## License 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 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. httparse-1.8.0/benches/parse.rs000064400000000000000000000075401046102023000145610ustar 00000000000000 use std::time::Duration; use criterion::{black_box, criterion_group, criterion_main, Criterion, Throughput}; const REQ_SHORT: &[u8] = b"\ GET / HTTP/1.0\r\n\ Host: example.com\r\n\ Cookie: session=60; user_id=1\r\n\r\n"; const REQ: &[u8] = b"\ GET /wp-content/uploads/2010/03/hello-kitty-darth-vader-pink.jpg HTTP/1.1\r\n\ Host: www.kittyhell.com\r\n\ User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; ja-JP-mac; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 Pathtraq/0.9\r\n\ Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n\ Accept-Language: ja,en-us;q=0.7,en;q=0.3\r\n\ Accept-Encoding: gzip,deflate\r\n\ Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7\r\n\ Keep-Alive: 115\r\n\ Connection: keep-alive\r\n\ Cookie: wp_ozh_wsa_visits=2; wp_ozh_wsa_visit_lasttime=xxxxxxxxxx; __utma=xxxxxxxxx.xxxxxxxxxx.xxxxxxxxxx.xxxxxxxxxx.xxxxxxxxxx.x; __utmz=xxxxxxxxx.xxxxxxxxxx.x.x.utmccn=(referral)|utmcsr=reader.livedoor.com|utmcct=/reader/|utmcmd=referral|padding=under256\r\n\r\n"; fn req(c: &mut Criterion) { let mut headers = [httparse::Header{ name: "", value: &[] }; 16]; let mut req = httparse::Request::new(&mut headers); c.benchmark_group("req") .throughput(Throughput::Bytes(REQ.len() as u64)) .bench_function("req", |b| b.iter(|| { assert_eq!(black_box(req.parse(REQ).unwrap()), httparse::Status::Complete(REQ.len())); })); } fn req_short(c: &mut Criterion) { let mut headers = [httparse::Header{ name: "", value: &[] }; 16]; let mut req = httparse::Request::new(&mut headers); c.benchmark_group("req_short") .throughput(Throughput::Bytes(REQ_SHORT.len() as u64)) .bench_function("req_short", |b| b.iter(|| { assert_eq!(black_box(req.parse(REQ_SHORT).unwrap()), httparse::Status::Complete(REQ_SHORT.len())); })); } const RESP_SHORT: &[u8] = b"\ HTTP/1.0 200 OK\r\n\ Date: Wed, 21 Oct 2015 07:28:00 GMT\r\n\ Set-Cookie: session=60; user_id=1\r\n\r\n"; // These particular headers don't all make semantic sense for a response, but they're syntactically valid. const RESP: &[u8] = b"\ HTTP/1.1 200 OK\r\n\ Date: Wed, 21 Oct 2015 07:28:00 GMT\r\n\ Host: www.kittyhell.com\r\n\ User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; ja-JP-mac; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 Pathtraq/0.9\r\n\ Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n\ Accept-Language: ja,en-us;q=0.7,en;q=0.3\r\n\ Accept-Encoding: gzip,deflate\r\n\ Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7\r\n\ Keep-Alive: 115\r\n\ Connection: keep-alive\r\n\ Cookie: wp_ozh_wsa_visits=2; wp_ozh_wsa_visit_lasttime=xxxxxxxxxx; __utma=xxxxxxxxx.xxxxxxxxxx.xxxxxxxxxx.xxxxxxxxxx.xxxxxxxxxx.x; __utmz=xxxxxxxxx.xxxxxxxxxx.x.x.utmccn=(referral)|utmcsr=reader.livedoor.com|utmcct=/reader/|utmcmd=referral|padding=under256\r\n\r\n"; fn resp(c: &mut Criterion) { let mut headers = [httparse::Header{ name: "", value: &[] }; 16]; let mut resp = httparse::Response::new(&mut headers); c.benchmark_group("resp") .throughput(Throughput::Bytes(RESP.len() as u64)) .bench_function("resp", |b| b.iter(|| { assert_eq!(black_box(resp.parse(RESP).unwrap()), httparse::Status::Complete(RESP.len())); })); } fn resp_short(c: &mut Criterion) { let mut headers = [httparse::Header{ name: "", value: &[] }; 16]; let mut resp = httparse::Response::new(&mut headers); c.benchmark_group("resp_short") .throughput(Throughput::Bytes(RESP_SHORT.len() as u64)) .bench_function("resp_short", |b| b.iter(|| { assert_eq!(black_box(resp.parse(RESP_SHORT).unwrap()), httparse::Status::Complete(RESP_SHORT.len())); })); } criterion_group!{ name = benches; config = Criterion::default().sample_size(100).measurement_time(Duration::from_secs(10)); targets = req, req_short, resp, resp_short } criterion_main!(benches); httparse-1.8.0/build.rs000064400000000000000000000112701046102023000131320ustar 00000000000000use std::env; //use std::ffi::OsString; //use std::process::Command; fn main() { // We don't currently need to check the Version anymore... // But leaving this in place in case we need to in the future. /* let rustc = env::var_os("RUSTC").unwrap_or(OsString::from("rustc")); let output = Command::new(&rustc) .arg("--version") .output() .expect("failed to check 'rustc --version'") .stdout; let version = String::from_utf8(output) .expect("rustc version output should be utf-8"); */ enable_new_features(/*&version*/); } fn enable_new_features(/*raw_version: &str*/) { /* let version = match Version::parse(raw_version) { Ok(version) => version, Err(err) => { println!("cargo:warning=failed to parse `rustc --version`: {}", err); return; } }; */ enable_simd(/*version*/); } fn enable_simd(/*version: Version*/) { if env::var_os("CARGO_FEATURE_STD").is_none() { println!("cargo:warning=building for no_std disables httparse SIMD"); return; } if env::var_os("CARGO_CFG_MIRI").is_some() { println!("cargo:warning=building for Miri disables httparse SIMD"); return; } let env_disable = "CARGO_CFG_HTTPARSE_DISABLE_SIMD"; if var_is(env_disable, "1") { println!("cargo:warning=detected {} environment variable, disabling SIMD", env_disable); return; } println!("cargo:rustc-cfg=httparse_simd"); // cfg(target_feature) isn't stable yet, but CARGO_CFG_TARGET_FEATURE has // a list... We aren't doing anything unsafe, since the is_x86_feature_detected // macro still checks in the actual lib, BUT! // // By peeking at the list here, we can change up slightly how we do feature // detection in the lib. If our features aren't in the feature list, we // stick with a cached runtime detection strategy. // // But if the features *are* in the list, we benefit from removing our cache, // 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); return; } let feature_list = match env::var_os("CARGO_CFG_TARGET_FEATURE") { Some(var) => match var.into_string() { Ok(s) => s, 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 }, }; let mut saw_sse42 = false; let mut saw_avx2 = false; for feature in feature_list.split(',') { let feature = feature.trim(); if !saw_sse42 && feature == "sse4.2" { saw_sse42 = true; println!("cargo:rustc-cfg=httparse_simd_target_feature_sse42"); } if !saw_avx2 && feature == "avx2" { saw_avx2 = true; println!("cargo:rustc-cfg=httparse_simd_target_feature_avx2"); } } } /* #[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] struct Version { major: u32, minor: u32, patch: u32, } impl Version { fn parse(mut s: &str) -> Result { if !s.starts_with("rustc ") { return Err(format!("unrecognized version string: {}", s)); } s = &s["rustc ".len()..]; let parts: Vec<&str> = s.split(".").collect(); if parts.len() < 3 { return Err(format!("not enough version parts: {:?}", parts)); } let mut num = String::new(); for c in parts[0].chars() { if !c.is_digit(10) { break; } num.push(c); } let major = num.parse::().map_err(|e| e.to_string())?; num.clear(); for c in parts[1].chars() { if !c.is_digit(10) { break; } num.push(c); } let minor = num.parse::().map_err(|e| e.to_string())?; num.clear(); for c in parts[2].chars() { if !c.is_digit(10) { break; } num.push(c); } let patch = num.parse::().map_err(|e| e.to_string())?; Ok(Version { major: major, minor: minor, patch: patch, }) } } */ fn var_is(key: &str, val: &str) -> bool { match env::var(key) { Ok(v) => v == val, Err(_) => false, } } httparse-1.8.0/src/iter.rs000064400000000000000000000047041046102023000135710ustar 00000000000000use core::slice; use core::convert::TryInto; use core::convert::TryFrom; pub struct Bytes<'a> { slice: &'a [u8], pos: usize } impl<'a> Bytes<'a> { #[inline] pub fn new(slice: &'a [u8]) -> Bytes<'a> { Bytes { slice, pos: 0 } } #[inline] pub fn pos(&self) -> usize { self.pos } #[inline] pub fn peek(&self) -> Option { self.peek_ahead(0) } #[inline] pub fn peek_ahead(&self, n: usize) -> Option { self.slice.get(self.pos + n).copied() } #[inline] pub fn peek_n>(&self, n: usize) -> Option { self.slice.get(self.pos..self.pos + n)?.try_into().ok() } #[inline] pub unsafe fn bump(&mut self) { debug_assert!(self.pos < self.slice.len(), "overflow"); self.pos += 1; } #[allow(unused)] #[inline] pub unsafe fn advance(&mut self, n: usize) { debug_assert!(self.pos + n <= self.slice.len(), "overflow"); self.pos += n; } #[inline] pub fn len(&self) -> usize { self.slice.len() } #[inline] pub fn slice(&mut self) -> &'a [u8] { // not moving position at all, so it's safe unsafe { self.slice_skip(0) } } #[inline] pub unsafe fn slice_skip(&mut self, skip: usize) -> &'a [u8] { debug_assert!(self.pos >= skip); let head_pos = self.pos - skip; let ptr = self.slice.as_ptr(); let head = slice::from_raw_parts(ptr, head_pos); let tail = slice::from_raw_parts(ptr.add(self.pos), self.slice.len() - self.pos); self.pos = 0; self.slice = tail; head } #[inline] pub unsafe fn advance_and_commit(&mut self, n: usize) { debug_assert!(self.pos + n <= self.slice.len(), "overflow"); self.pos += n; let ptr = self.slice.as_ptr(); let tail = slice::from_raw_parts(ptr.add(n), self.slice.len() - n); self.pos = 0; self.slice = tail; } } impl<'a> AsRef<[u8]> for Bytes<'a> { #[inline] fn as_ref(&self) -> &[u8] { &self.slice[self.pos..] } } impl<'a> Iterator for Bytes<'a> { type Item = u8; #[inline] fn next(&mut self) -> Option { if self.slice.len() > self.pos { let b = unsafe { *self.slice.get_unchecked(self.pos) }; self.pos += 1; Some(b) } else { None } } } httparse-1.8.0/src/lib.rs000064400000000000000000002364531046102023000134040ustar 00000000000000#![cfg_attr(not(feature = "std"), no_std)] #![deny(missing_docs)] #![cfg_attr(test, deny(warnings))] //! # httparse //! //! A push library for parsing HTTP/1.x requests and responses. //! //! The focus is on speed and safety. Unsafe code is used to keep parsing fast, //! but unsafety is contained in a submodule, with invariants enforced. The //! parsing internals use an `Iterator` instead of direct indexing, while //! skipping bounds checks. //! //! With Rust 1.27.0 or later, support for SIMD is enabled automatically. //! If building an executable to be run on multiple platforms, and thus //! not passing `target_feature` or `target_cpu` flags to the compiler, //! runtime detection can still detect SSE4.2 or AVX2 support to provide //! massive wins. //! //! If compiling for a specific target, remembering to include //! `-C target_cpu=native` allows the detection to become compile time checks, //! making it *even* faster. use core::{fmt, result, str}; use core::mem::{self, MaybeUninit}; use crate::iter::Bytes; mod iter; #[macro_use] mod macros; mod simd; /// Determines if byte is a token char. /// /// > ```notrust /// > token = 1*tchar /// > /// > tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" /// > / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" /// > / DIGIT / ALPHA /// > ; any VCHAR, except delimiters /// > ``` #[inline] fn is_token(b: u8) -> bool { b > 0x1F && b < 0x7F } // ASCII codes to accept URI string. // i.e. A-Z a-z 0-9 !#$%&'*+-._();:@=,/?[]~^ // TODO: Make a stricter checking for URI string? static URI_MAP: [bool; 256] = byte_map![ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // \0 \n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // commands 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // \w ! " # $ % & ' ( ) * + , - . / 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, // 0 1 2 3 4 5 6 7 8 9 : ; < = > ? 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // @ A B C D E F G H I J K L M N O 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // P Q R S T U V W X Y Z [ \ ] ^ _ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // ` a b c d e f g h i j k l m n o 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // p q r s t u v w x y z { | } ~ del // ====== Extended ASCII (aka. obs-text) ====== 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]; #[inline] fn is_uri_token(b: u8) -> bool { URI_MAP[b as usize] } static HEADER_NAME_MAP: [bool; 256] = byte_map![ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]; #[inline] fn is_header_name_token(b: u8) -> bool { HEADER_NAME_MAP[b as usize] } static HEADER_VALUE_MAP: [bool; 256] = byte_map![ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ]; #[inline] fn is_header_value_token(b: u8) -> bool { HEADER_VALUE_MAP[b as usize] } /// An error in parsing. #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum Error { /// Invalid byte in header name. HeaderName, /// Invalid byte in header value. HeaderValue, /// Invalid byte in new line. NewLine, /// Invalid byte in Response status. Status, /// Invalid byte where token is required. Token, /// Parsed more headers than provided buffer can contain. TooManyHeaders, /// Invalid byte in HTTP version. Version, } impl Error { #[inline] fn description_str(&self) -> &'static str { match *self { Error::HeaderName => "invalid header name", Error::HeaderValue => "invalid header value", Error::NewLine => "invalid new line", Error::Status => "invalid response status", Error::Token => "invalid token", Error::TooManyHeaders => "too many headers", Error::Version => "invalid HTTP version", } } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(self.description_str()) } } #[cfg(feature = "std")] impl std::error::Error for Error { fn description(&self) -> &str { self.description_str() } } /// An error in parsing a chunk size. // Note: Move this into the error enum once v2.0 is released. #[derive(Debug, PartialEq, Eq)] pub struct InvalidChunkSize; impl fmt::Display for InvalidChunkSize { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("invalid chunk size") } } /// A Result of any parsing action. /// /// If the input is invalid, an `Error` will be returned. Note that incomplete /// data is not considered invalid, and so will not return an error, but rather /// a `Ok(Status::Partial)`. pub type Result = result::Result, Error>; /// The result of a successful parse pass. /// /// `Complete` is used when the buffer contained the complete value. /// `Partial` is used when parsing did not reach the end of the expected value, /// but no invalid data was found. #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub enum Status { /// The completed result. Complete(T), /// A partial result. Partial } impl Status { /// Convenience method to check if status is complete. #[inline] pub fn is_complete(&self) -> bool { match *self { Status::Complete(..) => true, Status::Partial => false } } /// Convenience method to check if status is partial. #[inline] pub fn is_partial(&self) -> bool { match *self { Status::Complete(..) => false, Status::Partial => true } } /// Convenience method to unwrap a Complete value. Panics if the status is /// `Partial`. #[inline] pub fn unwrap(self) -> T { match self { Status::Complete(t) => t, Status::Partial => panic!("Tried to unwrap Status::Partial") } } } /// Parser configuration. #[derive(Clone, Debug, Default)] pub struct ParserConfig { allow_spaces_after_header_name_in_responses: bool, allow_obsolete_multiline_headers_in_responses: bool, allow_multiple_spaces_in_request_line_delimiters: bool, allow_multiple_spaces_in_response_status_delimiters: bool, ignore_invalid_headers_in_responses: bool, } 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 { self.allow_spaces_after_header_name_in_responses = value; self } /// Sets whether multiple spaces are allowed as delimiters in request lines. /// /// # Background /// /// The [latest version of the HTTP/1.1 spec][spec] allows implementations to parse multiple /// whitespace characters in place of the `SP` delimiters in the request line, including: /// /// > SP, HTAB, VT (%x0B), FF (%x0C), or bare CR /// /// This option relaxes the parser to allow for multiple spaces, but does *not* allow the /// request line to contain the other mentioned whitespace characters. /// /// [spec]: https://httpwg.org/http-core/draft-ietf-httpbis-messaging-latest.html#rfc.section.3.p.3 pub fn allow_multiple_spaces_in_request_line_delimiters(&mut self, value: bool) -> &mut Self { self.allow_multiple_spaces_in_request_line_delimiters = value; self } /// Whether multiple spaces are allowed as delimiters in request lines. pub fn multiple_spaces_in_request_line_delimiters_are_allowed(&self) -> bool { self.allow_multiple_spaces_in_request_line_delimiters } /// Sets whether multiple spaces are allowed as delimiters in response status lines. /// /// # Background /// /// The [latest version of the HTTP/1.1 spec][spec] allows implementations to parse multiple /// whitespace characters in place of the `SP` delimiters in the response status line, /// including: /// /// > SP, HTAB, VT (%x0B), FF (%x0C), or bare CR /// /// This option relaxes the parser to allow for multiple spaces, but does *not* allow the status /// 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 { self.allow_multiple_spaces_in_response_status_delimiters = value; self } /// Whether multiple spaces are allowed as delimiters in response status lines. pub fn multiple_spaces_in_response_status_delimiters_are_allowed(&self) -> bool { self.allow_multiple_spaces_in_response_status_delimiters } /// Sets whether obsolete multiline headers should be allowed. /// /// This is an obsolete part of HTTP/1. Use at your own risk. If you are /// building an HTTP library, the newlines (`\r` and `\n`) should be /// replaced by spaces before handing the header value to the user. /// /// # Example /// /// ```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 res = httparse::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!(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 { self.allow_obsolete_multiline_headers_in_responses = value; self } /// Whether obsolete multiline headers should be allowed. pub fn obsolete_multiline_headers_in_responses_are_allowed(&self) -> bool { self.allow_obsolete_multiline_headers_in_responses } /// Parses a request with the given config. pub fn parse_request<'headers, 'buf>( &self, request: &mut Request<'headers, 'buf>, buf: &'buf [u8], ) -> Result { request.parse_with_config(buf, self) } /// Parses a request with the given config and buffer for headers pub fn parse_request_with_uninit_headers<'headers, 'buf>( &self, request: &mut Request<'headers, 'buf>, buf: &'buf [u8], headers: &'headers mut [MaybeUninit>], ) -> Result { request.parse_with_config_and_uninit_headers(buf, self, headers) } /// Sets whether invalid header lines should be silently ignored in responses. /// /// This mimicks the behaviour of major browsers. You probably don't want this. /// You should only want this if you are implementing a proxy whose main /// purpose is to sit in front of browsers whose users access arbitrary content /// which may be malformed, and they expect everything that works without /// the proxy to keep working with the proxy. /// /// This option will prevent `ParserConfig::parse_response` from returning /// an error encountered when parsing a header, except if the error was caused /// by the character NUL (ASCII code 0), as Chrome specifically always reject /// those, or if the error was caused by a lone character `\r`, as Firefox and /// Chrome behave differently in that case. /// /// The ignorable errors are: /// * empty header names; /// * characters that are not allowed in header names, except for `\0` and `\r`; /// * when `allow_spaces_after_header_name_in_responses` is not enabled, /// spaces and tabs between the header name and the colon; /// * missing colon between header name and value; /// * when `allow_obsolete_multiline_headers_in_responses` is not enabled, /// headers using obsolete line folding. /// * characters that are not allowed in header values except for `\0` and `\r`. /// /// If an ignorable error is encountered, the parser tries to find the next /// line in the input to resume parsing the rest of the headers. As lines /// contributing to a header using obsolete line folding always start /// 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 { self.ignore_invalid_headers_in_responses = value; self } /// Parses a response with the given config. pub fn parse_response<'headers, 'buf>( &self, response: &mut Response<'headers, 'buf>, buf: &'buf [u8], ) -> Result { response.parse_with_config(buf, self) } /// Parses a response with the given config and buffer for headers pub fn parse_response_with_uninit_headers<'headers, 'buf>( &self, response: &mut Response<'headers, 'buf>, buf: &'buf [u8], headers: &'headers mut [MaybeUninit>], ) -> Result { response.parse_with_config_and_uninit_headers(buf, self, headers) } } /// A parsed Request. /// /// The optional values will be `None` if a parse was not complete, and did not /// parse the associated property. This allows you to inspect the parts that /// could be parsed, before reading more, in case you wish to exit early. /// /// # Example /// /// ```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 res = req.parse(buf).unwrap(); /// if res.is_partial() { /// match req.path { /// Some(ref path) => { /// // check router for path. /// // /404 doesn't exist? we could stop parsing /// }, /// None => { /// // must read more and parse again /// } /// } /// } /// ``` #[derive(Debug, Eq, PartialEq)] pub struct Request<'headers, 'buf> { /// The request method, such as `GET`. pub method: Option<&'buf str>, /// The request path, such as `/about-us`. pub path: Option<&'buf str>, /// The request minor version, such as `1` for `HTTP/1.1`. pub version: Option, /// The request headers. pub headers: &'headers mut [Header<'buf>] } impl<'h, 'b> Request<'h, 'b> { /// Creates a new Request, using a slice of headers you allocate. #[inline] pub fn new(headers: &'h mut [Header<'b>]) -> Request<'h, 'b> { Request { method: None, path: None, version: None, headers, } } fn parse_with_config_and_uninit_headers( &mut self, buf: &'b [u8], config: &ParserConfig, mut headers: &'h mut [MaybeUninit>], ) -> Result { let orig_len = buf.len(); let mut bytes = Bytes::new(buf); complete!(skip_empty_lines(&mut bytes)); const GET: [u8; 4] = *b"GET "; const POST: [u8; 4] = *b"POST"; let method = match bytes.peek_n::<[u8; 4]>(4) { Some(GET) => { unsafe { bytes.advance_and_commit(4); } "GET" } Some(POST) if bytes.peek_ahead(4) == Some(b' ') => { unsafe { bytes.advance_and_commit(5); } "POST" } _ => complete!(parse_token(&mut bytes)), }; self.method = Some(method); if config.allow_multiple_spaces_in_request_line_delimiters { complete!(skip_spaces(&mut bytes)); } self.path = Some(complete!(parse_uri(&mut bytes))); if config.allow_multiple_spaces_in_request_line_delimiters { complete!(skip_spaces(&mut bytes)); } self.version = Some(complete!(parse_version(&mut bytes))); newline!(bytes); let len = orig_len - bytes.len(); let headers_len = complete!(parse_headers_iter_uninit( &mut headers, &mut bytes, &ParserConfig::default(), )); /* SAFETY: see `parse_headers_iter_uninit` guarantees */ self.headers = unsafe { assume_init_slice(headers) }; Ok(Status::Complete(len + headers_len)) } /// Try to parse a buffer of bytes into the Request, /// except use an uninitialized slice of `Header`s. /// /// For more information, see `parse` pub fn parse_with_uninit_headers( &mut self, buf: &'b [u8], headers: &'h mut [MaybeUninit>], ) -> Result { self.parse_with_config_and_uninit_headers(buf, &Default::default(), headers) } fn parse_with_config(&mut self, buf: &'b [u8], config: &ParserConfig) -> Result { let headers = mem::replace(&mut self.headers, &mut []); /* SAFETY: see `parse_headers_iter_uninit` guarantees */ unsafe { let headers: *mut [Header<'_>] = headers; let headers = headers as *mut [MaybeUninit>]; match self.parse_with_config_and_uninit_headers(buf, config, &mut *headers) { Ok(Status::Complete(idx)) => Ok(Status::Complete(idx)), other => { // put the original headers back self.headers = &mut *(headers as *mut [Header<'_>]); other }, } } } /// Try to parse a buffer of bytes into the Request. /// /// Returns byte offset in `buf` to start of HTTP body. pub fn parse(&mut self, buf: &'b [u8]) -> Result { self.parse_with_config(buf, &Default::default()) } } #[inline] fn skip_empty_lines(bytes: &mut Bytes<'_>) -> Result<()> { loop { let b = bytes.peek(); match b { Some(b'\r') => { // there's `\r`, so it's safe to bump 1 pos unsafe { bytes.bump() }; expect!(bytes.next() == b'\n' => Err(Error::NewLine)); }, Some(b'\n') => { // there's `\n`, so it's safe to bump 1 pos unsafe { bytes.bump(); } }, Some(..) => { bytes.slice(); return Ok(Status::Complete(())); }, None => return Ok(Status::Partial) } } } #[inline] fn skip_spaces(bytes: &mut Bytes<'_>) -> Result<()> { loop { let b = bytes.peek(); match b { Some(b' ') => { // there's ` `, so it's safe to bump 1 pos unsafe { bytes.bump() }; } Some(..) => { bytes.slice(); return Ok(Status::Complete(())); } None => return Ok(Status::Partial), } } } /// A parsed Response. /// /// See `Request` docs for explanation of optional values. #[derive(Debug, Eq, PartialEq)] pub struct Response<'headers, 'buf> { /// The response minor version, such as `1` for `HTTP/1.1`. pub version: Option, /// The response code, such as `200`. pub code: Option, /// The response reason-phrase, such as `OK`. /// /// 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>] } impl<'h, 'b> Response<'h, 'b> { /// Creates a new `Response` using a slice of `Header`s you have allocated. #[inline] pub fn new(headers: &'h mut [Header<'b>]) -> Response<'h, 'b> { Response { version: None, code: None, reason: None, headers, } } /// Try to parse a buffer of bytes into this `Response`. pub fn parse(&mut self, buf: &'b [u8]) -> Result { self.parse_with_config(buf, &ParserConfig::default()) } fn parse_with_config(&mut self, buf: &'b [u8], config: &ParserConfig) -> Result { let headers = mem::replace(&mut self.headers, &mut []); unsafe { let headers: *mut [Header<'_>] = headers; let headers = headers as *mut [MaybeUninit>]; match self.parse_with_config_and_uninit_headers(buf, config, &mut *headers) { Ok(Status::Complete(idx)) => Ok(Status::Complete(idx)), other => { // put the original headers back self.headers = &mut *(headers as *mut [Header<'_>]); other }, } } } fn parse_with_config_and_uninit_headers( &mut self, buf: &'b [u8], config: &ParserConfig, mut headers: &'h mut [MaybeUninit>], ) -> Result { let orig_len = buf.len(); let mut bytes = Bytes::new(buf); complete!(skip_empty_lines(&mut bytes)); self.version = Some(complete!(parse_version(&mut bytes))); space!(bytes or Error::Version); if config.allow_multiple_spaces_in_response_status_delimiters { complete!(skip_spaces(&mut bytes)); } self.code = Some(complete!(parse_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 // optional (and preferred to be omitted) in HTTP2, we'll just // handle any response that doesn't include a reason-phrase, because // it's more lenient, and we don't care anyways. // // So, a SP means parse a reason-phrase. // A newline means go to headers. // Anything else we'll say is a malformed status. match next!(bytes) { b' ' => { if config.allow_multiple_spaces_in_response_status_delimiters { complete!(skip_spaces(&mut bytes)); } 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(""); } _ => return Err(Error::Status), } let len = orig_len - bytes.len(); let headers_len = complete!(parse_headers_iter_uninit( &mut headers, &mut bytes, config )); /* SAFETY: see `parse_headers_iter_uninit` guarantees */ self.headers = unsafe { assume_init_slice(headers) }; Ok(Status::Complete(len + headers_len)) } } /// Represents a parsed header. #[derive(Copy, Clone, Eq, PartialEq)] pub struct Header<'a> { /// The name portion of a header. /// /// A header name must be valid ASCII-US, so it's safe to store as a `&str`. pub name: &'a str, /// The value portion of a header. /// /// While headers **should** be ASCII-US, the specification allows for /// values that may not be, and so the value is stored as bytes. pub value: &'a [u8], } impl<'a> fmt::Debug for Header<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut f = f.debug_struct("Header"); f.field("name", &self.name); if let Ok(value) = str::from_utf8(self.value) { f.field("value", &value); } else { f.field("value", &self.value); } f.finish() } } /// An empty header, useful for constructing a `Header` array to pass in for /// parsing. /// /// # Example /// /// ``` /// let headers = [httparse::EMPTY_HEADER; 64]; /// ``` pub const EMPTY_HEADER: Header<'static> = Header { name: "", value: b"" }; #[inline] fn parse_version(bytes: &mut Bytes<'_>) -> Result { if let Some(eight) = bytes.peek_n::<[u8; 8]>(8) { unsafe { bytes.advance(8); } return match &eight { b"HTTP/1.0" => Ok(Status::Complete(0)), b"HTTP/1.1" => Ok(Status::Complete(1)), _ => Err(Error::Version), } } // else (but not in `else` because of borrow checker) // If there aren't at least 8 bytes, we still want to detect early // if this is a valid version or not. If it is, we'll return Partial. expect!(bytes.next() == b'H' => Err(Error::Version)); expect!(bytes.next() == b'T' => Err(Error::Version)); expect!(bytes.next() == b'T' => Err(Error::Version)); expect!(bytes.next() == b'P' => Err(Error::Version)); expect!(bytes.next() == b'/' => Err(Error::Version)); expect!(bytes.next() == b'1' => Err(Error::Version)); expect!(bytes.next() == b'.' => Err(Error::Version)); Ok(Status::Partial) } /// From [RFC 7230](https://tools.ietf.org/html/rfc7230): /// /// > ```notrust /// > reason-phrase = *( HTAB / SP / VCHAR / obs-text ) /// > HTAB = %x09 ; horizontal tab /// > VCHAR = %x21-7E ; visible (printing) characters /// > obs-text = %x80-FF /// > ``` /// /// > A.2. Changes from RFC 2616 /// > /// > Non-US-ASCII content in header fields and the reason phrase /// > has been obsoleted and made opaque (the TEXT rule was removed). #[inline] fn parse_reason<'a>(bytes: &mut Bytes<'a>) -> Result<&'a str> { let mut seen_obs_text = false; loop { let b = next!(bytes); if b == b'\r' { expect!(bytes.next() == b'\n' => Err(Error::Status)); return Ok(Status::Complete(unsafe { let bytes = bytes.slice_skip(2); if !seen_obs_text { // all bytes up till `i` must have been HTAB / SP / VCHAR str::from_utf8_unchecked(bytes) } else { // obs-text characters were found, so return the fallback empty string "" } })); } else if b == b'\n' { return Ok(Status::Complete(unsafe { let bytes = bytes.slice_skip(1); if !seen_obs_text { // all bytes up till `i` must have been HTAB / SP / VCHAR str::from_utf8_unchecked(bytes) } else { // obs-text characters were found, so return the fallback empty string "" } })); } else if !(b == 0x09 || b == b' ' || (0x21..=0x7E).contains(&b) || b >= 0x80) { return Err(Error::Status); } else if b >= 0x80 { seen_obs_text = true; } } } #[inline] fn parse_token<'a>(bytes: &mut Bytes<'a>) -> Result<&'a str> { let b = next!(bytes); if !is_token(b) { // First char must be a token char, it can't be a space which would indicate an empty token. return Err(Error::Token); } loop { let b = next!(bytes); if b == b' ' { return Ok(Status::Complete(unsafe { // all bytes up till `i` must have been `is_token`. str::from_utf8_unchecked(bytes.slice_skip(1)) })); } else if !is_token(b) { return Err(Error::Token); } } } #[inline] fn parse_uri<'a>(bytes: &mut Bytes<'a>) -> Result<&'a str> { let b = next!(bytes); if !is_uri_token(b) { // First char must be a URI char, it can't be a space which would indicate an empty path. return Err(Error::Token); } simd::match_uri_vectored(bytes); loop { let b = next!(bytes); if b == b' ' { return Ok(Status::Complete(unsafe { // all bytes up till `i` must have been `is_token`. str::from_utf8_unchecked(bytes.slice_skip(1)) })); } else if !is_uri_token(b) { return Err(Error::Token); } } } #[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)) } /// Parse a buffer of bytes as headers. /// /// The return value, if complete and successful, includes the index of the /// buffer that parsing stopped at, and a sliced reference to the parsed /// headers. The length of the slice will be equal to the number of properly /// parsed headers. /// /// # Example /// /// ``` /// 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"*/*" } /// ][..])))); /// ``` pub fn parse_headers<'b: 'h, 'h>( src: &'b [u8], 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, &ParserConfig::default())); Ok(Status::Complete((pos, dst))) } #[inline] fn parse_headers_iter<'a, 'b>( headers: &mut &mut [Header<'a>], bytes: &'b mut Bytes<'a>, config: &ParserConfig, ) -> Result { parse_headers_iter_uninit( /* SAFETY: see `parse_headers_iter_uninit` guarantees */ unsafe { deinit_slice_mut(headers) }, bytes, config, ) } unsafe fn deinit_slice_mut<'a, 'b, T>(s: &'a mut &'b mut [T]) -> &'a mut &'b mut [MaybeUninit] { let s: *mut &mut [T] = s; let s = s as *mut &mut [MaybeUninit]; &mut *s } unsafe fn assume_init_slice(s: &mut [MaybeUninit]) -> &mut [T] { let s: *mut [MaybeUninit] = s; let s = s as *mut [T]; &mut *s } /* Function which parsers headers into uninitialized buffer. * * Guarantees that it doesn't write garbage, so casting * &mut &mut [Header] -> &mut &mut [MaybeUninit
] * is safe here. * * Also it promises `headers` get shrunk to number of initialized headers, * so casting the other way around after calling this function is safe */ fn parse_headers_iter_uninit<'a, 'b>( headers: &mut &mut [MaybeUninit>], bytes: &'b mut Bytes<'a>, config: &ParserConfig, ) -> 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 * instructions in case of early return */ struct ShrinkOnDrop<'r1, 'r2, 'a> { headers: &'r1 mut &'r2 mut [MaybeUninit>], num_headers: usize, } impl<'r1, 'r2, 'a> Drop for ShrinkOnDrop<'r1, 'r2, 'a> { fn drop(&mut self) { let headers = mem::replace(self.headers, &mut []); /* SAFETY: num_headers is the number of initialized headers */ let headers = unsafe { headers.get_unchecked_mut(..self.num_headers) }; *self.headers = headers; } } let mut autoshrink = ShrinkOnDrop { headers, num_headers: 0, }; let mut count: usize = 0; let mut result = Err(Error::TooManyHeaders); let mut iter = autoshrink.headers.iter_mut(); macro_rules! maybe_continue_after_obsolete_line_folding { ($bytes:ident, $label:lifetime) => { if config.allow_obsolete_multiline_headers_in_responses { match $bytes.peek() { None => { // Next byte may be a space, in which case that header // is using obsolete line folding, so we may have more // whitespace to skip after colon. return Ok(Status::Partial); } Some(b' ') | Some(b'\t') => { // The space will be consumed next iteration. continue $label; } _ => { // There is another byte after the end of the line, // but it's not whitespace, so it's probably another // header or the final line return. This header is thus // empty. }, } } } } 'headers: loop { // Return the error `$err` if `ignore_invalid_headers_in_responses` // is false, otherwise find the end of the current line and resume // parsing on the next one. macro_rules! handle_invalid_char { ($bytes:ident, $b:ident, $err:ident) => { if !config.ignore_invalid_headers_in_responses { return Err(Error::$err); } let mut b = $b; loop { if b == b'\r' { expect!(bytes.next() == b'\n' => Err(Error::$err)); break; } if b == b'\n' { break; } if b == b'\0' { return Err(Error::$err); } b = next!($bytes); } count += $bytes.pos(); $bytes.slice(); continue 'headers; }; } // a newline here means the head is over! let b = next!(bytes); if b == b'\r' { expect!(bytes.next() == b'\n' => Err(Error::NewLine)); result = Ok(Status::Complete(count + bytes.pos())); break; } if b == b'\n' { result = Ok(Status::Complete(count + bytes.pos())); break; } if !is_header_name_token(b) { handle_invalid_char!(bytes, b, HeaderName); } // parse header name until colon let header_name: &str = 'name: loop { let mut b = next!(bytes); if is_header_name_token(b) { continue 'name; } count += bytes.pos(); let name = unsafe { str::from_utf8_unchecked(bytes.slice_skip(1)) }; if b == b':' { break 'name name; } if config.allow_spaces_after_header_name_in_responses { while b == b' ' || b == b'\t' { b = next!(bytes); if b == b':' { count += bytes.pos(); bytes.slice(); break 'name name; } } } handle_invalid_char!(bytes, b, HeaderName); }; let mut b; let value_slice = 'value: loop { // eat white space between colon and value 'whitespace_after_colon: loop { b = next!(bytes); if b == b' ' || b == b'\t' { count += bytes.pos(); bytes.slice(); continue 'whitespace_after_colon; } if is_header_value_token(b) { break 'whitespace_after_colon; } if b == b'\r' { expect!(bytes.next() == b'\n' => Err(Error::HeaderValue)); } else if b != b'\n' { handle_invalid_char!(bytes, b, HeaderValue); } maybe_continue_after_obsolete_line_folding!(bytes, 'whitespace_after_colon); count += bytes.pos(); let whitespace_slice = bytes.slice(); // This produces an empty slice that points to the beginning // of the whitespace. break 'value &whitespace_slice[0..0]; } 'value_lines: loop { // parse value till EOL simd::match_header_value_vectored(bytes); 'value_line: loop { if let Some(bytes8) = bytes.peek_n::<[u8; 8]>(8) { macro_rules! check { ($bytes:ident, $i:literal) => ({ b = $bytes[$i]; if !is_header_value_token(b) { unsafe { bytes.advance($i + 1); } break 'value_line; } }); } check!(bytes8, 0); check!(bytes8, 1); check!(bytes8, 2); check!(bytes8, 3); check!(bytes8, 4); check!(bytes8, 5); check!(bytes8, 6); check!(bytes8, 7); unsafe { bytes.advance(8); } continue 'value_line; } b = next!(bytes); if !is_header_value_token(b) { break 'value_line; } } //found_ctl let skip = if b == b'\r' { expect!(bytes.next() == b'\n' => Err(Error::HeaderValue)); 2 } else if b == b'\n' { 1 } else { handle_invalid_char!(bytes, b, HeaderValue); }; maybe_continue_after_obsolete_line_folding!(bytes, 'value_lines); count += bytes.pos(); // having just checked that a newline exists, it's safe to skip it. unsafe { break 'value bytes.slice_skip(skip); } } }; let uninit_header = match iter.next() { Some(header) => header, None => break 'headers }; // trim trailing whitespace in the header let header_value = if let Some(last_visible) = value_slice .iter() .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] } else { // There is no non-whitespace character. This can only happen when value_slice is // empty. value_slice }; *uninit_header = MaybeUninit::new(Header { name: header_name, value: header_value, }); autoshrink.num_headers += 1; } result } /// Parse a buffer of bytes as a chunk size. /// /// The return value, if complete and successful, includes the index of the /// buffer that parsing stopped at, and the size of the following chunk. /// /// # Example /// /// ``` /// 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)))); /// ``` pub fn parse_chunk_size(buf: &[u8]) -> result::Result, InvalidChunkSize> { const RADIX: u64 = 16; let mut bytes = Bytes::new(buf); let mut size = 0; let mut in_chunk_size = true; let mut in_ext = false; let mut count = 0; loop { let b = next!(bytes); match b { b'0' ..= b'9' if in_chunk_size => { if count > 15 { return Err(InvalidChunkSize); } count += 1; size *= RADIX; size += (b - b'0') as u64; }, b'a' ..= b'f' if in_chunk_size => { if count > 15 { return Err(InvalidChunkSize); } count += 1; size *= RADIX; size += (b + 10 - b'a') as u64; } b'A' ..= b'F' if in_chunk_size => { if count > 15 { return Err(InvalidChunkSize); } count += 1; size *= RADIX; size += (b + 10 - b'A') as u64; } 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; in_chunk_size = false; } // "Linear white space" is ignored between the chunk size and the // extension separator token (";") due to the "implied *LWS rule". b'\t' | b' ' if !in_ext && !in_chunk_size => {} // LWS can follow the chunk size, but no more digits can come b'\t' | b' ' if in_chunk_size => in_chunk_size = false, // We allow any arbitrary octet once we are in the extension, since // they all get ignored anyway. According to the HTTP spec, valid // extensions would have a more strict syntax: // (token ["=" (token | quoted-string)]) // but we gain nothing by rejecting an otherwise valid chunk size. _ if in_ext => {} // Finally, if we aren't in the extension and we're reading any // other octet, the chunk size line is invalid! _ => return Err(InvalidChunkSize), } } Ok(Status::Complete((bytes.pos(), size))) } #[cfg(test)] mod tests { use super::{Request, Response, Status, EMPTY_HEADER, parse_chunk_size}; const NUM_OF_HEADERS: usize = 4; macro_rules! req { ($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 } } ) } req! { test_request_simple, b"GET / HTTP/1.1\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(), 0); } } req! { test_request_simple_with_query_params, b"GET /thing?data=a HTTP/1.1\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/thing?data=a"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 0); } } req! { test_request_simple_with_whatwg_query_params, b"GET /thing?data=a^ HTTP/1.1\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/thing?data=a^"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 0); } } req! { test_request_headers, b"GET / HTTP/1.1\r\nHost: foo.com\r\nCookie: \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(), 2); 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""); } } req! { test_request_headers_optional_whitespace, b"GET / HTTP/1.1\r\nHost: \tfoo.com\t \r\nCookie: \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(), 2); 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""); } } req! { // test the scalar parsing test_request_header_value_htab_short, b"GET / HTTP/1.1\r\nUser-Agent: some\tagent\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(), 1); assert_eq!(req.headers[0].name, "User-Agent"); assert_eq!(req.headers[0].value, b"some\tagent"); } } req! { // test the sse42 parsing test_request_header_value_htab_med, b"GET / HTTP/1.1\r\nUser-Agent: 1234567890some\tagent\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(), 1); assert_eq!(req.headers[0].name, "User-Agent"); assert_eq!(req.headers[0].value, b"1234567890some\tagent"); } } req! { // test the avx2 parsing test_request_header_value_htab_long, b"GET / HTTP/1.1\r\nUser-Agent: 1234567890some\t1234567890agent1234567890\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(), 1); assert_eq!(req.headers[0].name, "User-Agent"); assert_eq!(req.headers[0].value, &b"1234567890some\t1234567890agent1234567890"[..]); } } req! { test_request_headers_max, b"GET / HTTP/1.1\r\nA: A\r\nB: B\r\nC: C\r\nD: D\r\n\r\n", |req| { assert_eq!(req.headers.len(), NUM_OF_HEADERS); } } req! { test_request_multibyte, b"GET / HTTP/1.1\r\nHost: foo.com\r\nUser-Agent: \xe3\x81\xb2\xe3/1.0\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(), 2); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"foo.com"); assert_eq!(req.headers[1].name, "User-Agent"); assert_eq!(req.headers[1].value, b"\xe3\x81\xb2\xe3/1.0"); } } req! { test_request_partial, b"GET / HTTP/1.1\r\n\r", Ok(Status::Partial), |_req| {} } req! { test_request_partial_version, b"GET / HTTP/1.", Ok(Status::Partial), |_req| {} } req! { test_request_partial_parses_headers_as_much_as_it_can, b"GET / HTTP/1.1\r\nHost: yolo\r\n", Ok(crate::Status::Partial), |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), NUM_OF_HEADERS); // doesn't slice since not Complete assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"yolo"); } } req! { test_request_newlines, b"GET / HTTP/1.1\nHost: foo.bar\n\n", |_r| {} } req! { test_request_empty_lines_prefix, b"\r\n\r\nGET / HTTP/1.1\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(), 0); } } req! { test_request_empty_lines_prefix_lf_only, b"\n\nGET / HTTP/1.1\n\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 0); } } req! { test_request_path_backslash, b"\n\nGET /\\?wayne\\=5 HTTP/1.1\n\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/\\?wayne\\=5"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 0); } } req! { test_request_with_invalid_token_delimiter, b"GET\n/ HTTP/1.1\r\nHost: foo.bar\r\n\r\n", Err(crate::Error::Token), |_r| {} } req! { test_request_with_invalid_but_short_version, b"GET / HTTP/1!", Err(crate::Error::Version), |_r| {} } req! { test_request_with_empty_method, b" / HTTP/1.1\r\n\r\n", Err(crate::Error::Token), |_r| {} } req! { test_request_with_empty_path, b"GET HTTP/1.1\r\n\r\n", Err(crate::Error::Token), |_r| {} } req! { test_request_with_empty_method_and_path, b" HTTP/1.1\r\n\r\n", Err(crate::Error::Token), |_r| {} } macro_rules! res { ($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 } } ) } res! { test_response_simple, b"HTTP/1.1 200 OK\r\n\r\n", |res| { assert_eq!(res.version.unwrap(), 1); assert_eq!(res.code.unwrap(), 200); assert_eq!(res.reason.unwrap(), "OK"); } } res! { test_response_newlines, b"HTTP/1.0 403 Forbidden\nServer: foo.bar\n\n", |_r| {} } res! { test_response_reason_missing, b"HTTP/1.1 200 \r\n\r\n", |res| { assert_eq!(res.version.unwrap(), 1); assert_eq!(res.code.unwrap(), 200); assert_eq!(res.reason.unwrap(), ""); } } res! { test_response_reason_missing_no_space, b"HTTP/1.1 200\r\n\r\n", |res| { assert_eq!(res.version.unwrap(), 1); assert_eq!(res.code.unwrap(), 200); assert_eq!(res.reason.unwrap(), ""); } } res! { test_response_reason_missing_no_space_with_headers, b"HTTP/1.1 200\r\nFoo: bar\r\n\r\n", |res| { assert_eq!(res.version.unwrap(), 1); assert_eq!(res.code.unwrap(), 200); assert_eq!(res.reason.unwrap(), ""); assert_eq!(res.headers.len(), 1); assert_eq!(res.headers[0].name, "Foo"); assert_eq!(res.headers[0].value, b"bar"); } } res! { test_response_reason_with_space_and_tab, b"HTTP/1.1 101 Switching Protocols\t\r\n\r\n", |res| { assert_eq!(res.version.unwrap(), 1); assert_eq!(res.code.unwrap(), 101); assert_eq!(res.reason.unwrap(), "Switching Protocols\t"); } } static RESPONSE_REASON_WITH_OBS_TEXT_BYTE: &[u8] = b"HTTP/1.1 200 X\xFFZ\r\n\r\n"; res! { test_response_reason_with_obsolete_text_byte, RESPONSE_REASON_WITH_OBS_TEXT_BYTE, |res| { assert_eq!(res.version.unwrap(), 1); assert_eq!(res.code.unwrap(), 200); // Empty string fallback in case of obs-text assert_eq!(res.reason.unwrap(), ""); } } res! { test_response_reason_with_nul_byte, b"HTTP/1.1 200 \x00\r\n\r\n", Err(crate::Error::Status), |_res| {} } res! { test_response_version_missing_space, b"HTTP/1.1", Ok(Status::Partial), |_res| {} } res! { test_response_code_missing_space, b"HTTP/1.1 200", Ok(Status::Partial), |_res| {} } res! { test_response_partial_parses_headers_as_much_as_it_can, b"HTTP/1.1 200 OK\r\nServer: yolo\r\n", Ok(crate::Status::Partial), |res| { assert_eq!(res.version.unwrap(), 1); assert_eq!(res.code.unwrap(), 200); assert_eq!(res.reason.unwrap(), "OK"); assert_eq!(res.headers.len(), NUM_OF_HEADERS); // doesn't slice since not Complete assert_eq!(res.headers[0].name, "Server"); assert_eq!(res.headers[0].value, b"yolo"); } } res! { test_response_empty_lines_prefix_lf_only, b"\n\nHTTP/1.1 200 OK\n\n", |_res| {} } res! { test_response_no_cr, b"HTTP/1.0 200\nContent-type: text/html\n\n", |res| { assert_eq!(res.version.unwrap(), 0); assert_eq!(res.code.unwrap(), 200); assert_eq!(res.reason.unwrap(), ""); assert_eq!(res.headers.len(), 1); assert_eq!(res.headers[0].name, "Content-type"); assert_eq!(res.headers[0].value, b"text/html"); } } static RESPONSE_WITH_WHITESPACE_BETWEEN_HEADER_NAME_AND_COLON: &[u8] = b"HTTP/1.1 200 OK\r\nAccess-Control-Allow-Credentials : true\r\nBread: baguette\r\n\r\n"; #[test] fn test_forbid_response_with_whitespace_between_header_name_and_colon() { let mut headers = [EMPTY_HEADER; 2]; let mut response = Response::new(&mut headers[..]); let result = response.parse(RESPONSE_WITH_WHITESPACE_BETWEEN_HEADER_NAME_AND_COLON); assert_eq!(result, Err(crate::Error::HeaderName)); } #[test] fn test_allow_response_with_whitespace_between_header_name_and_colon() { let mut headers = [EMPTY_HEADER; 2]; 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); assert_eq!(result, Ok(Status::Complete(77))); assert_eq!(response.version.unwrap(), 1); assert_eq!(response.code.unwrap(), 200); assert_eq!(response.reason.unwrap(), "OK"); assert_eq!(response.headers.len(), 2); assert_eq!(response.headers[0].name, "Access-Control-Allow-Credentials"); assert_eq!(response.headers[0].value, &b"true"[..]); assert_eq!(response.headers[1].name, "Bread"); assert_eq!(response.headers[1].value, &b"baguette"[..]); } #[test] fn test_ignore_header_line_with_whitespaces_after_header_name() { let mut headers = [EMPTY_HEADER; 2]; 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); assert_eq!(result, Ok(Status::Complete(77))); assert_eq!(response.version.unwrap(), 1); assert_eq!(response.code.unwrap(), 200); assert_eq!(response.reason.unwrap(), "OK"); assert_eq!(response.headers.len(), 1); assert_eq!(response.headers[0].name, "Bread"); assert_eq!(response.headers[0].value, &b"baguette"[..]); } static REQUEST_WITH_WHITESPACE_BETWEEN_HEADER_NAME_AND_COLON: &[u8] = b"GET / HTTP/1.1\r\nHost : localhost\r\n\r\n"; #[test] fn test_forbid_request_with_whitespace_between_header_name_and_colon() { let mut headers = [EMPTY_HEADER; 1]; let mut request = Request::new(&mut headers[..]); let result = request.parse(REQUEST_WITH_WHITESPACE_BETWEEN_HEADER_NAME_AND_COLON); assert_eq!(result, Err(crate::Error::HeaderName)); } static RESPONSE_WITH_OBSOLETE_LINE_FOLDING_AT_START: &[u8] = b"HTTP/1.1 200 OK\r\nLine-Folded-Header: \r\n \r\n hello there\r\n\r\n"; #[test] fn test_forbid_response_with_obsolete_line_folding_at_start() { let mut headers = [EMPTY_HEADER; 1]; let mut response = Response::new(&mut headers[..]); let result = response.parse(RESPONSE_WITH_OBSOLETE_LINE_FOLDING_AT_START); assert_eq!(result, Err(crate::Error::HeaderName)); } #[test] fn test_allow_response_with_obsolete_line_folding_at_start() { let mut headers = [EMPTY_HEADER; 1]; 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_AT_START); 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"); assert_eq!(response.headers.len(), 1); assert_eq!(response.headers[0].name, "Line-Folded-Header"); assert_eq!(response.headers[0].value, &b"hello there"[..]); } static RESPONSE_WITH_OBSOLETE_LINE_FOLDING_AT_END: &[u8] = b"HTTP/1.1 200 OK\r\nLine-Folded-Header: hello there\r\n \r\n \r\n\r\n"; #[test] fn test_forbid_response_with_obsolete_line_folding_at_end() { let mut headers = [EMPTY_HEADER; 1]; let mut response = Response::new(&mut headers[..]); let result = response.parse(RESPONSE_WITH_OBSOLETE_LINE_FOLDING_AT_END); assert_eq!(result, Err(crate::Error::HeaderName)); } #[test] fn test_allow_response_with_obsolete_line_folding_at_end() { let mut headers = [EMPTY_HEADER; 1]; 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_AT_END); 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"); assert_eq!(response.headers.len(), 1); assert_eq!(response.headers[0].name, "Line-Folded-Header"); assert_eq!(response.headers[0].value, &b"hello there"[..]); } static RESPONSE_WITH_OBSOLETE_LINE_FOLDING_IN_MIDDLE: &[u8] = b"HTTP/1.1 200 OK\r\nLine-Folded-Header: hello \r\n \r\n there\r\n\r\n"; #[test] fn test_forbid_response_with_obsolete_line_folding_in_middle() { let mut headers = [EMPTY_HEADER; 1]; let mut response = Response::new(&mut headers[..]); let result = response.parse(RESPONSE_WITH_OBSOLETE_LINE_FOLDING_IN_MIDDLE); assert_eq!(result, Err(crate::Error::HeaderName)); } #[test] fn test_allow_response_with_obsolete_line_folding_in_middle() { let mut headers = [EMPTY_HEADER; 1]; 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_MIDDLE); 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"); assert_eq!(response.headers.len(), 1); assert_eq!(response.headers[0].name, "Line-Folded-Header"); assert_eq!(response.headers[0].value, &b"hello \r\n \r\n there"[..]); } static RESPONSE_WITH_OBSOLETE_LINE_FOLDING_IN_EMPTY_HEADER: &[u8] = b"HTTP/1.1 200 OK\r\nLine-Folded-Header: \r\n \r\n \r\n\r\n"; #[test] fn test_forbid_response_with_obsolete_line_folding_in_empty_header() { let mut headers = [EMPTY_HEADER; 1]; let mut response = Response::new(&mut headers[..]); let result = response.parse(RESPONSE_WITH_OBSOLETE_LINE_FOLDING_IN_EMPTY_HEADER); assert_eq!(result, Err(crate::Error::HeaderName)); } #[test] fn test_allow_response_with_obsolete_line_folding_in_empty_header() { let mut headers = [EMPTY_HEADER; 1]; 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); 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"); assert_eq!(response.headers.len(), 1); assert_eq!(response.headers[0].name, "Line-Folded-Header"); assert_eq!(response.headers[0].value, &b""[..]); } #[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"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, std::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"; #[test] fn test_forbid_response_with_multiple_space_delimiters() { let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS]; let mut response = Response::new(&mut headers[..]); let result = response.parse(RESPONSE_WITH_MULTIPLE_SPACE_DELIMITERS); assert_eq!(result, Err(crate::Error::Status)); } #[test] fn test_allow_response_with_multiple_space_delimiters() { let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS]; let mut response = Response::new(&mut headers[..]); let result = crate::ParserConfig::default() .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!(response.version.unwrap(), 1); assert_eq!(response.code.unwrap(), 200); assert_eq!(response.reason.unwrap(), "OK"); assert_eq!(response.headers.len(), 0); } /// 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"; #[test] fn test_forbid_response_with_weird_whitespace_delimiters() { let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS]; let mut response = Response::new(&mut headers[..]); let result = response.parse(RESPONSE_WITH_WEIRD_WHITESPACE_DELIMITERS); assert_eq!(result, Err(crate::Error::Status)); } #[test] fn test_still_forbid_response_with_weird_whitespace_delimiters() { let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS]; let mut response = Response::new(&mut headers[..]); let result = crate::ParserConfig::default() .allow_multiple_spaces_in_response_status_delimiters(true) .parse_response(&mut response, RESPONSE_WITH_WEIRD_WHITESPACE_DELIMITERS); assert_eq!(result, Err(crate::Error::Status)); } 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() { let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS]; let mut request = Request::new(&mut headers[..]); let result = request.parse(REQUEST_WITH_MULTIPLE_SPACE_DELIMITERS); assert_eq!(result, Err(crate::Error::Token)); } #[test] fn test_allow_request_with_multiple_space_delimiters() { let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS]; let mut request = Request::new(&mut headers[..]); let result = crate::ParserConfig::default() .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!(request.method.unwrap(), "GET"); assert_eq!(request.path.unwrap(), "/"); assert_eq!(request.version.unwrap(), 1); assert_eq!(request.headers.len(), 0); } /// 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"; #[test] fn test_forbid_request_with_weird_whitespace_delimiters() { let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS]; let mut request = Request::new(&mut headers[..]); let result = request.parse(REQUEST_WITH_WEIRD_WHITESPACE_DELIMITERS); assert_eq!(result, Err(crate::Error::Token)); } #[test] fn test_still_forbid_request_with_weird_whitespace_delimiters() { let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS]; let mut request = Request::new(&mut headers[..]); let result = crate::ParserConfig::default() .allow_multiple_spaces_in_request_line_delimiters(true) .parse_request(&mut request, REQUEST_WITH_WEIRD_WHITESPACE_DELIMITERS); assert_eq!(result, Err(crate::Error::Token)); } static REQUEST_WITH_MULTIPLE_SPACES_AND_BAD_PATH: &[u8] = b"GET /foo>ohno HTTP/1.1\r\n\r\n"; #[test] fn test_request_with_multiple_spaces_and_bad_path() { let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS]; let mut request = Request::new(&mut headers[..]); let result = crate::ParserConfig::default() .allow_multiple_spaces_in_request_line_delimiters(true) .parse_request(&mut request, REQUEST_WITH_MULTIPLE_SPACES_AND_BAD_PATH); assert_eq!(result, Err(crate::Error::Token)); } static RESPONSE_WITH_SPACES_IN_CODE: &[u8] = b"HTTP/1.1 99 200 OK\r\n\r\n"; #[test] fn test_response_with_spaces_in_code() { let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS]; let mut response = Response::new(&mut headers[..]); let result = crate::ParserConfig::default() .allow_multiple_spaces_in_response_status_delimiters(true) .parse_response(&mut response, RESPONSE_WITH_SPACES_IN_CODE); assert_eq!(result, Err(crate::Error::Status)); } #[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"; let mut headers = [EMPTY_HEADER; 2]; 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); assert_eq!(result, Err(crate::Error::HeaderName)); let result = crate::ParserConfig::default() .ignore_invalid_headers_in_responses(true) .parse_response(&mut response, RESPONSE); assert_eq!(result, Ok(Status::Complete(45))); assert_eq!(response.version.unwrap(), 1); assert_eq!(response.code.unwrap(), 200); assert_eq!(response.reason.unwrap(), "OK"); assert_eq!(response.headers.len(), 1); assert_eq!(response.headers[0].name, "Bread"); assert_eq!(response.headers[0].value, &b"baguette"[..]); } #[test] fn test_request_with_whitespace_between_header_name_and_colon() { const REQUEST: &[u8] = b"GET / HTTP/1.1\r\nAccess-Control-Allow-Credentials : true\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() .allow_spaces_after_header_name_in_responses(true) .parse_request(&mut request, REQUEST); 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)); } #[test] fn test_response_with_invalid_char_between_header_name_and_colon() { const RESPONSE: &[u8] = b"HTTP/1.1 200 OK\r\nAccess-Control-Allow-Credentials\xFF : true\r\nBread: baguette\r\n\r\n"; let mut headers = [EMPTY_HEADER; 2]; 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); assert_eq!(result, Err(crate::Error::HeaderName)); let result = crate::ParserConfig::default() .ignore_invalid_headers_in_responses(true) .parse_response(&mut response, RESPONSE); assert_eq!(result, Ok(Status::Complete(79))); assert_eq!(response.version.unwrap(), 1); assert_eq!(response.code.unwrap(), 200); assert_eq!(response.reason.unwrap(), "OK"); assert_eq!(response.headers.len(), 1); assert_eq!(response.headers[0].name, "Bread"); assert_eq!(response.headers[0].value, &b"baguette"[..]); } #[test] fn test_ignore_header_line_with_missing_colon() { const RESPONSE: &[u8] = b"HTTP/1.1 200 OK\r\nAccess-Control-Allow-Credentials\r\nBread: baguette\r\n\r\n"; let mut headers = [EMPTY_HEADER; 2]; let mut response = Response::new(&mut headers[..]); let result = crate::ParserConfig::default() .parse_response(&mut response, RESPONSE); assert_eq!(result, Err(crate::Error::HeaderName)); let result = crate::ParserConfig::default() .ignore_invalid_headers_in_responses(true) .parse_response(&mut response, RESPONSE); assert_eq!(result, Ok(Status::Complete(70))); assert_eq!(response.version.unwrap(), 1); assert_eq!(response.code.unwrap(), 200); assert_eq!(response.reason.unwrap(), "OK"); assert_eq!(response.headers.len(), 1); assert_eq!(response.headers[0].name, "Bread"); assert_eq!(response.headers[0].value, &b"baguette"[..]); } #[test] fn test_header_with_missing_colon_with_folding() { const RESPONSE: &[u8] = b"HTTP/1.1 200 OK\r\nAccess-Control-Allow-Credentials \r\n hello\r\nBread: baguette\r\n\r\n"; let mut headers = [EMPTY_HEADER; 2]; let mut response = Response::new(&mut headers[..]); let result = crate::ParserConfig::default() .allow_obsolete_multiline_headers_in_responses(true) .allow_spaces_after_header_name_in_responses(true) .parse_response(&mut response, RESPONSE); assert_eq!(result, Err(crate::Error::HeaderName)); let result = crate::ParserConfig::default() .ignore_invalid_headers_in_responses(true) .parse_response(&mut response, RESPONSE); assert_eq!(result, Ok(Status::Complete(81))); assert_eq!(response.version.unwrap(), 1); assert_eq!(response.code.unwrap(), 200); assert_eq!(response.reason.unwrap(), "OK"); assert_eq!(response.headers.len(), 1); assert_eq!(response.headers[0].name, "Bread"); assert_eq!(response.headers[0].value, &b"baguette"[..]); } #[test] fn test_header_with_nul_in_header_name() { const RESPONSE: &[u8] = b"HTTP/1.1 200 OK\r\nAccess-Control-Allow-Cred\0entials: hello\r\nBread: baguette\r\n\r\n"; let mut headers = [EMPTY_HEADER; 2]; let mut response = Response::new(&mut headers[..]); let result = crate::ParserConfig::default() .parse_response(&mut response, RESPONSE); assert_eq!(result, Err(crate::Error::HeaderName)); let result = crate::ParserConfig::default() .ignore_invalid_headers_in_responses(true) .parse_response(&mut response, RESPONSE); assert_eq!(result, Err(crate::Error::HeaderName)); } #[test] fn test_header_with_cr_in_header_name() { const RESPONSE: &[u8] = b"HTTP/1.1 200 OK\r\nAccess-Control-Allow-Cred\rentials: hello\r\nBread: baguette\r\n\r\n"; let mut headers = [EMPTY_HEADER; 2]; let mut response = Response::new(&mut headers[..]); let result = crate::ParserConfig::default() .parse_response(&mut response, RESPONSE); assert_eq!(result, Err(crate::Error::HeaderName)); let result = crate::ParserConfig::default() .ignore_invalid_headers_in_responses(true) .parse_response(&mut response, RESPONSE); assert_eq!(result, Err(crate::Error::HeaderName)); } #[test] fn test_header_with_nul_in_whitespace_before_colon() { const RESPONSE: &[u8] = b"HTTP/1.1 200 OK\r\nAccess-Control-Allow-Credentials \0: hello\r\nBread: baguette\r\n\r\n"; let mut headers = [EMPTY_HEADER; 2]; 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); assert_eq!(result, Err(crate::Error::HeaderName)); let result = crate::ParserConfig::default() .allow_spaces_after_header_name_in_responses(true) .ignore_invalid_headers_in_responses(true) .parse_response(&mut response, RESPONSE); assert_eq!(result, Err(crate::Error::HeaderName)); } #[test] fn test_header_with_nul_in_value() { const RESPONSE: &[u8] = b"HTTP/1.1 200 OK\r\nAccess-Control-Allow-Credentials: hell\0o\r\nBread: baguette\r\n\r\n"; let mut headers = [EMPTY_HEADER; 2]; let mut response = Response::new(&mut headers[..]); let result = crate::ParserConfig::default() .parse_response(&mut response, RESPONSE); assert_eq!(result, Err(crate::Error::HeaderValue)); let result = crate::ParserConfig::default() .ignore_invalid_headers_in_responses(true) .parse_response(&mut response, RESPONSE); assert_eq!(result, Err(crate::Error::HeaderValue)); } #[test] fn test_header_with_invalid_char_in_value() { const RESPONSE: &[u8] = b"HTTP/1.1 200 OK\r\nAccess-Control-Allow-Credentials: hell\x01o\r\nBread: baguette\r\n\r\n"; let mut headers = [EMPTY_HEADER; 2]; let mut response = Response::new(&mut headers[..]); let result = crate::ParserConfig::default() .parse_response(&mut response, RESPONSE); assert_eq!(result, Err(crate::Error::HeaderValue)); let result = crate::ParserConfig::default() .ignore_invalid_headers_in_responses(true) .parse_response(&mut response, RESPONSE); assert_eq!(result, Ok(Status::Complete(78))); assert_eq!(response.version.unwrap(), 1); assert_eq!(response.code.unwrap(), 200); assert_eq!(response.reason.unwrap(), "OK"); assert_eq!(response.headers.len(), 1); assert_eq!(response.headers[0].name, "Bread"); assert_eq!(response.headers[0].value, &b"baguette"[..]); } #[test] fn test_header_with_invalid_char_in_value_with_folding() { const RESPONSE: &[u8] = b"HTTP/1.1 200 OK\r\nAccess-Control-Allow-Credentials: hell\x01o \n world!\r\nBread: baguette\r\n\r\n"; let mut headers = [EMPTY_HEADER; 2]; let mut response = Response::new(&mut headers[..]); let result = crate::ParserConfig::default() .parse_response(&mut response, RESPONSE); assert_eq!(result, Err(crate::Error::HeaderValue)); let result = crate::ParserConfig::default() .ignore_invalid_headers_in_responses(true) .parse_response(&mut response, RESPONSE); assert_eq!(result, Ok(Status::Complete(88))); assert_eq!(response.version.unwrap(), 1); assert_eq!(response.code.unwrap(), 200); assert_eq!(response.reason.unwrap(), "OK"); assert_eq!(response.headers.len(), 1); assert_eq!(response.headers[0].name, "Bread"); assert_eq!(response.headers[0].value, &b"baguette"[..]); } } httparse-1.8.0/src/macros.rs000064400000000000000000000023051046102023000141050ustar 00000000000000///! Utility macros macro_rules! next { ($bytes:ident) => ({ match $bytes.next() { Some(b) => b, None => return Ok(Status::Partial) } }) } macro_rules! expect { ($bytes:ident.next() == $pat:pat => $ret:expr) => { expect!(next!($bytes) => $pat |? $ret) }; ($e:expr => $pat:pat |? $ret:expr) => { match $e { v@$pat => v, _ => return $ret } }; } macro_rules! complete { ($e:expr) => { match $e? { Status::Complete(v) => v, Status::Partial => return Ok(Status::Partial) } } } macro_rules! byte_map { ($($flag:expr,)*) => ([ $($flag != 0,)* ]) } macro_rules! space { ($bytes:ident or $err:expr) => ({ expect!($bytes.next() == b' ' => Err($err)); $bytes.slice(); }) } macro_rules! newline { ($bytes:ident) => ({ match next!($bytes) { b'\r' => { expect!($bytes.next() == b'\n' => Err(Error::NewLine)); $bytes.slice(); }, b'\n' => { $bytes.slice(); }, _ => return Err(Error::NewLine) } }) } httparse-1.8.0/src/simd/avx2.rs000064400000000000000000000120661046102023000144420ustar 00000000000000use crate::iter::Bytes; pub enum Scan { /// Returned when an implementation finds a noteworthy token. Found, /// Returned when an implementation couldn't keep running because the input was too short. TooShort, } pub unsafe fn parse_uri_batch_32(bytes: &mut Bytes) -> Scan { while bytes.as_ref().len() >= 32 { let advance = match_url_char_32_avx(bytes.as_ref()); bytes.advance(advance); if advance != 32 { return Scan::Found; } } Scan::TooShort } #[cfg(target_arch = "x86_64")] #[target_feature(enable = "avx2")] #[inline] #[allow(non_snake_case, overflowing_literals)] unsafe fn match_url_char_32_avx(buf: &[u8]) -> usize { debug_assert!(buf.len() >= 32); /* #[cfg(target_arch = "x86")] use core::arch::x86::*; #[cfg(target_arch = "x86_64")] */ use core::arch::x86_64::*; let ptr = buf.as_ptr(); let LSH: __m256i = _mm256_set1_epi8(0x0f); // See comment in sse42::match_url_char_16_sse. let URI: __m256i = _mm256_setr_epi8( 0xf8, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf4, 0xfc, 0xf4, 0x7c, 0xf8, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf4, 0xfc, 0xf4, 0x7c, ); let ARF: __m256i = _mm256_setr_epi8( 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ); let data = _mm256_lddqu_si256(ptr as *const _); let rbms = _mm256_shuffle_epi8(URI, data); let cols = _mm256_and_si256(LSH, _mm256_srli_epi16(data, 4)); let bits = _mm256_and_si256(_mm256_shuffle_epi8(ARF, cols), rbms); let v = _mm256_cmpeq_epi8(bits, _mm256_setzero_si256()); let r = 0xffff_ffff_0000_0000 | _mm256_movemask_epi8(v) as u64; _tzcnt_u64(r) as usize } #[cfg(target_arch = "x86")] unsafe fn match_url_char_32_avx(_: &[u8]) -> usize { unreachable!("AVX2 detection should be disabled for x86"); } pub unsafe fn match_header_value_batch_32(bytes: &mut Bytes) -> Scan { while bytes.as_ref().len() >= 32 { let advance = match_header_value_char_32_avx(bytes.as_ref()); bytes.advance(advance); if advance != 32 { return Scan::Found; } } Scan::TooShort } #[cfg(target_arch = "x86_64")] #[target_feature(enable = "avx2")] #[inline] #[allow(non_snake_case)] unsafe fn match_header_value_char_32_avx(buf: &[u8]) -> usize { debug_assert!(buf.len() >= 32); /* #[cfg(target_arch = "x86")] use core::arch::x86::*; #[cfg(target_arch = "x86_64")] */ use core::arch::x86_64::*; let ptr = buf.as_ptr(); // %x09 %x20-%x7e %x80-%xff let TAB: __m256i = _mm256_set1_epi8(0x09); let DEL: __m256i = _mm256_set1_epi8(0x7f); let LOW: __m256i = _mm256_set1_epi8(0x20); let dat = _mm256_lddqu_si256(ptr as *const _); // unsigned comparison dat >= LOW let low = _mm256_cmpeq_epi8(_mm256_max_epu8(dat, LOW), dat); let tab = _mm256_cmpeq_epi8(dat, TAB); let del = _mm256_cmpeq_epi8(dat, DEL); let bit = _mm256_andnot_si256(del, _mm256_or_si256(low, tab)); let rev = _mm256_cmpeq_epi8(bit, _mm256_setzero_si256()); let res = 0xffff_ffff_0000_0000 | _mm256_movemask_epi8(rev) as u64; _tzcnt_u64(res) as usize } #[cfg(target_arch = "x86")] unsafe fn match_header_value_char_32_avx(_: &[u8]) -> usize { unreachable!("AVX2 detection should be disabled for x86"); } #[test] fn avx2_code_matches_uri_chars_table() { match super::detect() { super::AVX_2 | super::AVX_2_AND_SSE_42 => {}, _ => return, } unsafe { assert!(byte_is_allowed(b'_', parse_uri_batch_32)); for (b, allowed) in crate::URI_MAP.iter().cloned().enumerate() { assert_eq!( byte_is_allowed(b as u8, parse_uri_batch_32), allowed, "byte_is_allowed({:?}) should be {:?}", b, allowed, ); } } } #[test] fn avx2_code_matches_header_value_chars_table() { match super::detect() { super::AVX_2 | super::AVX_2_AND_SSE_42 => {}, _ => return, } unsafe { assert!(byte_is_allowed(b'_', match_header_value_batch_32)); for (b, allowed) in crate::HEADER_VALUE_MAP.iter().cloned().enumerate() { assert_eq!( byte_is_allowed(b as u8, match_header_value_batch_32), allowed, "byte_is_allowed({:?}) should be {:?}", b, allowed, ); } } } #[cfg(test)] unsafe fn byte_is_allowed(byte: u8, f: unsafe fn(bytes: &mut Bytes<'_>) -> Scan) -> 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'_', ]; let mut bytes = Bytes::new(&slice); f(&mut bytes); match bytes.pos() { 32 => true, 26 => false, _ => unreachable!(), } } httparse-1.8.0/src/simd/fallback.rs000064400000000000000000000003071046102023000153140ustar 00000000000000use crate::iter::Bytes; // Fallbacks that do nothing... #[inline(always)] pub fn match_uri_vectored(_: &mut Bytes<'_>) {} #[inline(always)] pub fn match_header_value_vectored(_: &mut Bytes<'_>) {} httparse-1.8.0/src/simd/mod.rs000064400000000000000000000161671046102023000143470ustar 00000000000000#[cfg(not(all( httparse_simd, any( target_arch = "x86", target_arch = "x86_64", ), )))] mod fallback; #[cfg(not(all( httparse_simd, any( target_arch = "x86", target_arch = "x86_64", ), )))] pub use self::fallback::*; #[cfg(all( httparse_simd, any( target_arch = "x86", target_arch = "x86_64", ), ))] mod sse42; #[cfg(all( httparse_simd, any( httparse_simd_target_feature_avx2, not(httparse_simd_target_feature_sse42), ), any( target_arch = "x86", target_arch = "x86_64", ), ))] mod avx2; #[cfg(all( httparse_simd, any( target_arch = "x86", target_arch = "x86_64", ), ))] pub const SSE_42: usize = 1; #[cfg(all( httparse_simd, any(not(httparse_simd_target_feature_sse42), httparse_simd_target_feature_avx2), any( target_arch = "x86", target_arch = "x86_64", ), ))] pub const AVX_2: usize = 2; #[cfg(all( httparse_simd, any( not(httparse_simd_target_feature_sse42), httparse_simd_target_feature_avx2, test, ), any( target_arch = "x86", target_arch = "x86_64", ), ))] pub const AVX_2_AND_SSE_42: usize = 3; #[cfg(all( httparse_simd, any( target_arch = "x86", target_arch = "x86_64", ), ))] const NONE: usize = std::usize::MAX; #[cfg(all( httparse_simd, not(any( httparse_simd_target_feature_sse42, httparse_simd_target_feature_avx2, )), any( target_arch = "x86", target_arch = "x86_64", ), ))] mod runtime { //! Runtime detection of simd features. Used when the build script //! doesn't notice any target features at build time. //! //! While `is_x86_feature_detected!` has it's own caching built-in, //! at least in 1.27.0, the functions don't inline, leaving using it //! actually *slower* than just using the scalar fallback. use core::sync::atomic::{AtomicUsize, Ordering}; static FEATURE: AtomicUsize = AtomicUsize::new(0); const INIT: usize = 0; pub fn detect() -> usize { let feat = FEATURE.load(Ordering::Relaxed); if feat == INIT { if cfg!(target_arch = "x86_64") && is_x86_feature_detected!("avx2") { if is_x86_feature_detected!("sse4.2") { FEATURE.store(super::AVX_2_AND_SSE_42, Ordering::Relaxed); return super::AVX_2_AND_SSE_42; } else { FEATURE.store(super::AVX_2, Ordering::Relaxed); return super::AVX_2; } } else if is_x86_feature_detected!("sse4.2") { FEATURE.store(super::SSE_42, Ordering::Relaxed); return super::SSE_42; } else { FEATURE.store(super::NONE, Ordering::Relaxed); } } feat } pub fn match_uri_vectored(bytes: &mut crate::iter::Bytes) { unsafe { match detect() { super::SSE_42 => super::sse42::parse_uri_batch_16(bytes), super::AVX_2 => { super::avx2::parse_uri_batch_32(bytes); }, super::AVX_2_AND_SSE_42 => { if let super::avx2::Scan::Found = super::avx2::parse_uri_batch_32(bytes) { return; } super::sse42::parse_uri_batch_16(bytes) }, _ => () } } // else do nothing } pub fn match_header_value_vectored(bytes: &mut crate::iter::Bytes) { unsafe { match detect() { super::SSE_42 => super::sse42::match_header_value_batch_16(bytes), super::AVX_2 => { super::avx2::match_header_value_batch_32(bytes); }, super::AVX_2_AND_SSE_42 => { if let super::avx2::Scan::Found = super::avx2::match_header_value_batch_32(bytes) { return; } super::sse42::match_header_value_batch_16(bytes) }, _ => () } } // else do nothing } } #[cfg(all( httparse_simd, not(any( httparse_simd_target_feature_sse42, httparse_simd_target_feature_avx2, )), any( target_arch = "x86", target_arch = "x86_64", ), ))] pub use self::runtime::*; #[cfg(all( httparse_simd, httparse_simd_target_feature_sse42, not(httparse_simd_target_feature_avx2), any( target_arch = "x86", target_arch = "x86_64", ), ))] mod sse42_compile_time { pub fn match_uri_vectored(bytes: &mut crate::iter::Bytes) { if detect() == super::SSE_42 { unsafe { super::sse42::parse_uri_batch_16(bytes); } } // else do nothing } pub fn match_header_value_vectored(bytes: &mut crate::iter::Bytes) { if detect() == super::SSE_42 { unsafe { super::sse42::match_header_value_batch_16(bytes); } } // else do nothing } pub fn detect() -> usize { if is_x86_feature_detected!("sse4.2") { super::SSE_42 } else { super::NONE } } } #[cfg(all( httparse_simd, httparse_simd_target_feature_sse42, not(httparse_simd_target_feature_avx2), 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", ), ))] mod avx2_compile_time { pub fn match_uri_vectored(bytes: &mut crate::iter::Bytes) { // do both, since avx2 only works when bytes.len() >= 32 if detect() == super::AVX_2_AND_SSE_42 { unsafe { super::avx2::parse_uri_batch_32(bytes); } } if detect() == super::SSE_42 { unsafe { super::sse42::parse_uri_batch_16(bytes); } } // else do nothing } pub fn match_header_value_vectored(bytes: &mut crate::iter::Bytes) { // do both, since avx2 only works when bytes.len() >= 32 if detect() == super::AVX_2_AND_SSE_42 { let scanned = unsafe { super::avx2::match_header_value_batch_32(bytes) }; if let super::avx2::Scan::Found = scanned { return; } } if detect() == super::SSE_42 { unsafe { super::sse42::match_header_value_batch_16(bytes); } } // else do nothing } pub fn detect() -> usize { if cfg!(target_arch = "x86_64") && is_x86_feature_detected!("avx2") { super::AVX_2_AND_SSE_42 } else if is_x86_feature_detected!("sse4.2") { super::SSE_42 } else { super::NONE } } } #[cfg(all( httparse_simd, httparse_simd_target_feature_avx2, any( target_arch = "x86", target_arch = "x86_64", ), ))] pub use self::avx2_compile_time::*; httparse-1.8.0/src/simd/sse42.rs000064400000000000000000000110301046102023000145100ustar 00000000000000use crate::iter::Bytes; pub unsafe fn parse_uri_batch_16(bytes: &mut Bytes) { while bytes.as_ref().len() >= 16 { let advance = match_url_char_16_sse(bytes.as_ref()); bytes.advance(advance); if advance != 16 { break; } } } #[target_feature(enable = "sse4.2")] #[allow(non_snake_case, overflowing_literals)] unsafe fn match_url_char_16_sse(buf: &[u8]) -> usize { debug_assert!(buf.len() >= 16); #[cfg(target_arch = "x86")] use core::arch::x86::*; #[cfg(target_arch = "x86_64")] use core::arch::x86_64::*; let ptr = buf.as_ptr(); let LSH: __m128i = _mm_set1_epi8(0x0f); // The first 0xf8 corresponds to the 8 first rows of the first column // of URI_MAP in the crate's root, with the first row corresponding to bit 0 // and the 8th row corresponding to bit 7. // The 8 first rows give 0 0 0 1 1 1 1 1, which is 0xf8 (with least // significant digit on the left). // // Another example just to drive the point home: in column 15, '>' is // rejected, so the values are 0 0 1 0 1 1 1 1, which gives us 0xf4. // // Thanks to Vlad Krasnov for explaining this stuff to us mere mortals in // a GitHub comment! // // https://github.com/seanmonstar/httparse/pull/89#issuecomment-807039219 let URI: __m128i = _mm_setr_epi8( 0xf8, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf4, 0xfc, 0xf4, 0x7c, ); let ARF: __m128i = _mm_setr_epi8( 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ); let data = _mm_lddqu_si128(ptr as *const _); let rbms = _mm_shuffle_epi8(URI, data); let cols = _mm_and_si128(LSH, _mm_srli_epi16(data, 4)); let bits = _mm_and_si128(_mm_shuffle_epi8(ARF, cols), rbms); let v = _mm_cmpeq_epi8(bits, _mm_setzero_si128()); let r = 0xffff_0000 | _mm_movemask_epi8(v) as u32; _tzcnt_u32(r) as usize } pub unsafe fn match_header_value_batch_16(bytes: &mut Bytes) { while bytes.as_ref().len() >= 16 { let advance = match_header_value_char_16_sse(bytes.as_ref()); bytes.advance(advance); if advance != 16 { break; } } } #[target_feature(enable = "sse4.2")] #[allow(non_snake_case)] unsafe fn match_header_value_char_16_sse(buf: &[u8]) -> usize { debug_assert!(buf.len() >= 16); #[cfg(target_arch = "x86")] use core::arch::x86::*; #[cfg(target_arch = "x86_64")] use core::arch::x86_64::*; let ptr = buf.as_ptr(); // %x09 %x20-%x7e %x80-%xff let TAB: __m128i = _mm_set1_epi8(0x09); let DEL: __m128i = _mm_set1_epi8(0x7f); let LOW: __m128i = _mm_set1_epi8(0x20); let dat = _mm_lddqu_si128(ptr as *const _); // unsigned comparison dat >= LOW let low = _mm_cmpeq_epi8(_mm_max_epu8(dat, LOW), dat); let tab = _mm_cmpeq_epi8(dat, TAB); let del = _mm_cmpeq_epi8(dat, DEL); let bit = _mm_andnot_si128(del, _mm_or_si128(low, tab)); let rev = _mm_cmpeq_epi8(bit, _mm_setzero_si128()); let res = 0xffff_0000 | _mm_movemask_epi8(rev) as u32; _tzcnt_u32(res) as usize } #[test] fn sse_code_matches_uri_chars_table() { match super::detect() { super::SSE_42 | super::AVX_2_AND_SSE_42 => {}, _ => return, } unsafe { assert!(byte_is_allowed(b'_', parse_uri_batch_16)); for (b, allowed) in crate::URI_MAP.iter().cloned().enumerate() { assert_eq!( byte_is_allowed(b as u8, parse_uri_batch_16), allowed, "byte_is_allowed({:?}) should be {:?}", b, allowed, ); } } } #[test] fn sse_code_matches_header_value_chars_table() { match super::detect() { super::SSE_42 | super::AVX_2_AND_SSE_42 => {}, _ => return, } unsafe { assert!(byte_is_allowed(b'_', match_header_value_batch_16)); for (b, allowed) in crate::HEADER_VALUE_MAP.iter().cloned().enumerate() { assert_eq!( byte_is_allowed(b as u8, match_header_value_batch_16), allowed, "byte_is_allowed({:?}) should be {:?}", b, allowed, ); } } } #[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'_', ]; let mut bytes = Bytes::new(&slice); f(&mut bytes); match bytes.pos() { 16 => true, 10 => false, _ => unreachable!(), } } httparse-1.8.0/tests/uri.rs000064400000000000000000003110261046102023000137760ustar 00000000000000 use httparse::{Error, Request, Status, EMPTY_HEADER}; const NUM_OF_HEADERS: usize = 4; macro_rules! req { ($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 } } ) } req! { urltest_001, b"GET /bar;par?b HTTP/1.1\r\nHost: foo\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/bar;par?b"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"foo"); } } req! { urltest_002, b"GET /x HTTP/1.1\r\nHost: test\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/x"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"test"); } } req! { urltest_003, b"GET /x HTTP/1.1\r\nHost: test\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/x"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"test"); } } req! { urltest_004, b"GET /foo/foo.com HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/foo.com"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_005, b"GET /foo/:foo.com HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/:foo.com"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_006, b"GET /foo/foo.com HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/foo.com"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_007, b"GET foo.com HTTP/1.1\r\nHost: \r\n\r\n", Err(Error::Token), |_r| {} } req! { urltest_008, b"GET /%20b%20?%20d%20 HTTP/1.1\r\nHost: f\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/%20b%20?%20d%20"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"f"); } } req! { urltest_009, b"GET x x HTTP/1.1\r\nHost: \r\n\r\n", Err(Error::Version), |_r| {} } req! { urltest_010, b"GET /c HTTP/1.1\r\nHost: f\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/c"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"f"); } } req! { urltest_011, b"GET /c HTTP/1.1\r\nHost: f\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/c"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"f"); } } req! { urltest_012, b"GET /c HTTP/1.1\r\nHost: f\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/c"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"f"); } } req! { urltest_013, b"GET /c HTTP/1.1\r\nHost: f\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/c"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"f"); } } req! { urltest_014, b"GET /c HTTP/1.1\r\nHost: f\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/c"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"f"); } } req! { urltest_015, b"GET /foo/bar HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/bar"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_016, b"GET /foo/bar HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/bar"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_017, b"GET /foo/:foo.com/ HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/:foo.com/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_018, b"GET /foo/:foo.com/ HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/:foo.com/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_019, b"GET /foo/: HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/:"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_020, b"GET /foo/:a HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/:a"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_021, b"GET /foo/:/ HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/:/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_022, b"GET /foo/:/ HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/:/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_023, b"GET /foo/: HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/:"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_024, b"GET /foo/bar HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/bar"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_025, b"GET /foo/bar HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/bar"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_026, b"GET /foo/bar HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/bar"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_027, b"GET /foo/bar HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/bar"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_028, b"GET /foo/bar HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/bar"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_029, b"GET /foo/:23 HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/:23"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_030, b"GET /:23 HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/:23"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_031, b"GET /foo/:: HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/::"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_032, b"GET /foo/::23 HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/::23"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_033, b"GET /d HTTP/1.1\r\nHost: c\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/d"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"c"); } } req! { urltest_034, b"GET /foo/:@c:29 HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/:@c:29"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_035, b"GET //@ HTTP/1.1\r\nHost: foo.com\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(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"foo.com"); } } req! { urltest_036, b"GET /b:c/d@foo.com/ HTTP/1.1\r\nHost: a\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/b:c/d@foo.com/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"a"); } } req! { urltest_037, b"GET /bar.com/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/bar.com/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_038, b"GET /////// HTTP/1.1\r\nHost: \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(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_039, b"GET ///////bar.com/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "///////bar.com/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_040, b"GET //:///// HTTP/1.1\r\nHost: \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(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_041, b"GET /foo HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_042, b"GET /bar HTTP/1.1\r\nHost: foo\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/bar"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"foo"); } } req! { urltest_043, b"GET /path;a??e HTTP/1.1\r\nHost: foo\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/path;a??e"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"foo"); } } req! { urltest_044, b"GET /abcd?efgh?ijkl HTTP/1.1\r\nHost: foo\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/abcd?efgh?ijkl"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"foo"); } } req! { urltest_045, b"GET /abcd HTTP/1.1\r\nHost: foo\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/abcd"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"foo"); } } req! { urltest_046, b"GET /foo/[61:24:74]:98 HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/[61:24:74]:98"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_047, b"GET /foo/[61:27]/:foo HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/[61:27]/:foo"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_048, b"GET /example.com/ HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/example.com/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_049, b"GET /example.com/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/example.com/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_050, b"GET /example.com/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/example.com/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_051, b"GET /example.com/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/example.com/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_052, b"GET /example.com/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/example.com/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_053, b"GET /example.com/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/example.com/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_054, b"GET /example.com/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/example.com/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_055, b"GET /foo/example.com/ HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/example.com/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_056, b"GET example.com/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "example.com/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_057, b"GET example.com/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "example.com/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_058, b"GET example.com/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "example.com/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_059, b"GET example.com/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "example.com/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_060, b"GET example.com/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "example.com/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_061, b"GET /a/b/c HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/a/b/c"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_062, b"GET /a/%20/c HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/a/%20/c"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_063, b"GET /a%2fc HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/a%2fc"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_064, b"GET /a/%2f/c HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/a/%2f/c"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_065, b"GET /foo/bar HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/bar"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_066, b"GET text/html,test HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "text/html,test"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_067, b"GET 1234567890 HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "1234567890"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_068, b"GET /c:/foo/bar.html HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/c:/foo/bar.html"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_069, b"GET /c:////foo/bar.html HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/c:////foo/bar.html"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_070, b"GET /C:/foo/bar HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/C:/foo/bar"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_071, b"GET /C:/foo/bar HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/C:/foo/bar"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_072, b"GET /C:/foo/bar HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/C:/foo/bar"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_073, b"GET /file HTTP/1.1\r\nHost: server\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/file"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"server"); } } req! { urltest_074, b"GET /file HTTP/1.1\r\nHost: server\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/file"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"server"); } } req! { urltest_075, b"GET /file HTTP/1.1\r\nHost: server\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/file"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"server"); } } req! { urltest_076, b"GET /foo/bar.txt HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/bar.txt"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_077, b"GET /home/me HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/home/me"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_078, b"GET /test HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/test"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_079, b"GET /test HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/test"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_080, b"GET /tmp/mock/test HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/tmp/mock/test"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_081, b"GET /tmp/mock/test HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/tmp/mock/test"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_082, b"GET /foo HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_083, b"GET /.foo HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/.foo"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_084, b"GET /foo/ HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_085, b"GET /foo/ HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_086, b"GET /foo/ HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_087, b"GET /foo/ HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_088, b"GET /foo/..bar HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/..bar"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_089, b"GET /foo/ton HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/ton"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_090, b"GET /a HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/a"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_091, b"GET /ton HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/ton"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_092, b"GET /foo/ HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_093, b"GET /foo/%2e%2 HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/%2e%2"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_094, b"GET /%2e.bar HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/%2e.bar"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_095, b"GET // HTTP/1.1\r\nHost: example.com\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(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_096, b"GET /foo/ HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_097, b"GET /foo/bar/ HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/bar/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_098, b"GET /foo HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_099, b"GET /%20foo HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/%20foo"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_100, b"GET /foo% HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo%"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_101, b"GET /foo%2 HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo%2"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_102, b"GET /foo%2zbar HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo%2zbar"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_103, b"GET /foo%2%C3%82%C2%A9zbar HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo%2%C3%82%C2%A9zbar"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_104, b"GET /foo%41%7a HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo%41%7a"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_105, b"GET /foo%C2%91%91 HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo%C2%91%91"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_106, b"GET /foo%00%51 HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo%00%51"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_107, b"GET /(%28:%3A%29) HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/(%28:%3A%29)"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_108, b"GET /%3A%3a%3C%3c HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/%3A%3a%3C%3c"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_109, b"GET /foobar HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foobar"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_110, b"GET //foo//bar HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "//foo//bar"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_111, b"GET /%7Ffp3%3Eju%3Dduvgw%3Dd HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/%7Ffp3%3Eju%3Dduvgw%3Dd"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_112, b"GET /@asdf%40 HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/@asdf%40"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } 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", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/%E4%BD%A0%E5%A5%BD%E4%BD%A0%E5%A5%BD"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_114, b"GET /%E2%80%A5/foo HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/%E2%80%A5/foo"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_115, b"GET /%EF%BB%BF/foo HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/%EF%BB%BF/foo"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_116, b"GET /%E2%80%AE/foo/%E2%80%AD/bar HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/%E2%80%AE/foo/%E2%80%AD/bar"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_117, b"GET /foo?bar=baz HTTP/1.1\r\nHost: www.google.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo?bar=baz"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"www.google.com"); } } req! { urltest_118, b"GET /foo?bar=baz HTTP/1.1\r\nHost: www.google.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo?bar=baz"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"www.google.com"); } } req! { urltest_119, b"GET test HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "test"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_120, b"GET /foo%2Ehtml HTTP/1.1\r\nHost: www\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo%2Ehtml"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"www"); } } req! { urltest_121, b"GET /foo/html HTTP/1.1\r\nHost: www\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/html"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"www"); } } req! { urltest_122, b"GET /foo HTTP/1.1\r\nHost: www.google.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"www.google.com"); } } req! { urltest_123, b"GET /example.com/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/example.com/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_124, b"GET /example.com/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/example.com/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_125, b"GET /example.com/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/example.com/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_126, b"GET /example.com/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/example.com/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_127, b"GET /example.com/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/example.com/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_128, b"GET /example.com/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/example.com/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_129, b"GET example.com/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "example.com/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_130, b"GET example.com/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "example.com/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_131, b"GET example.com/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "example.com/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_132, b"GET example.com/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "example.com/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_133, b"GET example.com/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "example.com/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_134, b"GET /test.txt HTTP/1.1\r\nHost: www.example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/test.txt"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"www.example.com"); } } req! { urltest_135, b"GET /test.txt HTTP/1.1\r\nHost: www.example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/test.txt"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"www.example.com"); } } req! { urltest_136, b"GET /test.txt HTTP/1.1\r\nHost: www.example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/test.txt"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"www.example.com"); } } req! { urltest_137, b"GET /test.txt HTTP/1.1\r\nHost: www.example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/test.txt"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"www.example.com"); } } req! { urltest_138, b"GET /aaa/test.txt HTTP/1.1\r\nHost: www.example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/aaa/test.txt"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"www.example.com"); } } req! { urltest_139, b"GET /test.txt HTTP/1.1\r\nHost: www.example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/test.txt"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"www.example.com"); } } req! { urltest_140, b"GET /%E4%B8%AD/test.txt HTTP/1.1\r\nHost: www.example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/%E4%B8%AD/test.txt"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"www.example.com"); } } req! { urltest_141, b"GET /... HTTP/1.1\r\nHost: \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(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_142, b"GET /a HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/a"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_143, b"GET /%EF%BF%BD?%EF%BF%BD HTTP/1.1\r\nHost: x\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/%EF%BF%BD?%EF%BF%BD"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"x"); } } req! { urltest_144, b"GET /bar HTTP/1.1\r\nHost: example.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/bar"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.com"); } } req! { urltest_145, b"GET test HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "test"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_146, b"GET x@x.com HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "x@x.com"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_147, b"GET , HTTP/1.1\r\nHost: \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(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_148, b"GET blank HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "blank"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_149, b"GET test?test HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "test?test"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_150, b"GET /%60%7B%7D?`{} HTTP/1.1\r\nHost: h\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/%60%7B%7D?`{}"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"h"); } } req! { urltest_151, b"GET /?%27 HTTP/1.1\r\nHost: host\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/?%27"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"host"); } } req! { urltest_152, b"GET /?' HTTP/1.1\r\nHost: host\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(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"host"); } } req! { urltest_153, b"GET /some/path HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/some/path"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_154, b"GET /smth HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/smth"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_155, b"GET /some/path HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/some/path"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_156, b"GET /pa/i HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/pa/i"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_157, b"GET /i HTTP/1.1\r\nHost: ho\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/i"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"ho"); } } req! { urltest_158, b"GET /pa/i HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/pa/i"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_159, b"GET /i HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/i"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_160, b"GET /i HTTP/1.1\r\nHost: ho\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/i"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"ho"); } } req! { urltest_161, b"GET /i HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/i"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_162, b"GET /i HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/i"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_163, b"GET /i HTTP/1.1\r\nHost: ho\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/i"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"ho"); } } req! { urltest_164, b"GET /i HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/i"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_165, b"GET /pa/pa?i HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/pa/pa?i"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_166, b"GET /pa?i HTTP/1.1\r\nHost: ho\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/pa?i"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"ho"); } } req! { urltest_167, b"GET /pa/pa?i HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/pa/pa?i"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_168, b"GET sd HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "sd"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_169, b"GET sd/sd HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "sd/sd"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_170, b"GET /pa/pa HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/pa/pa"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_171, b"GET /pa HTTP/1.1\r\nHost: ho\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/pa"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"ho"); } } req! { urltest_172, b"GET /pa/pa HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/pa/pa"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_173, b"GET /x HTTP/1.1\r\nHost: %C3%B1\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/x"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"%C3%B1"); } } req! { urltest_174, b"GET \\.\\./ HTTP/1.1\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(), 0); } } req! { urltest_175, b"GET :a@example.net HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), ":a@example.net"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_176, b"GET %NBD HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "%NBD"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_177, b"GET %1G HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "%1G"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_178, b"GET /relative_import.html HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/relative_import.html"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"127.0.0.1"); } } req! { urltest_179, b"GET /?foo=%7B%22abc%22 HTTP/1.1\r\nHost: facebook.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/?foo=%7B%22abc%22"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"facebook.com"); } } req! { urltest_180, b"GET /jqueryui@1.2.3 HTTP/1.1\r\nHost: localhost\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/jqueryui@1.2.3"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"localhost"); } } req! { urltest_181, b"GET /path?query HTTP/1.1\r\nHost: host\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/path?query"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"host"); } } req! { urltest_182, b"GET /foo/bar?a=b&c=d HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/bar?a=b&c=d"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_183, b"GET /foo/bar??a=b&c=d HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/bar??a=b&c=d"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_184, b"GET /foo/bar HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/bar"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_185, b"GET /baz?qux HTTP/1.1\r\nHost: foo.bar\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/baz?qux"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"foo.bar"); } } req! { urltest_186, b"GET /baz?qux HTTP/1.1\r\nHost: foo.bar\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/baz?qux"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"foo.bar"); } } req! { urltest_187, b"GET /baz?qux HTTP/1.1\r\nHost: foo.bar\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/baz?qux"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"foo.bar"); } } req! { urltest_188, b"GET /baz?qux HTTP/1.1\r\nHost: foo.bar\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/baz?qux"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"foo.bar"); } } req! { urltest_189, b"GET /baz?qux HTTP/1.1\r\nHost: foo.bar\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/baz?qux"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"foo.bar"); } } req! { urltest_190, b"GET /C%3A/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/C%3A/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_191, b"GET /C%7C/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/C%7C/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } 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", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/C:/Users/Domenic/Dropbox/GitHub/tmpvar/jsdom/test/level2/html/files/pix/submit.gif"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_193, b"GET /C:/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/C:/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_194, b"GET /C:/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/C:/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_195, b"GET /d: HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/d:"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_196, b"GET /d:/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/d:/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_197, b"GET /test?test HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/test?test"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_198, b"GET /test?test HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/test?test"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_199, b"GET /test?x HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/test?x"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_200, b"GET /test?x HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/test?x"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_201, b"GET /test?test HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/test?test"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_202, b"GET /test?test HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/test?test"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_203, b"GET /?fox HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/?fox"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_204, b"GET /localhost//cat HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/localhost//cat"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_205, b"GET /localhost//cat HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/localhost//cat"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_206, b"GET /mouse HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/mouse"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_207, b"GET /pig HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/pig"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_208, b"GET /pig HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/pig"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_209, b"GET /pig HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/pig"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_210, b"GET /localhost//pig HTTP/1.1\r\nHost: lion\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/localhost//pig"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"lion"); } } req! { urltest_211, b"GET /rooibos HTTP/1.1\r\nHost: tea\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/rooibos"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"tea"); } } req! { urltest_212, b"GET /?chai HTTP/1.1\r\nHost: tea\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/?chai"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"tea"); } } req! { urltest_213, b"GET /C: HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/C:"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_214, b"GET /C: HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/C:"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_215, b"GET /C: HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/C:"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_216, b"GET /C:/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/C:/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_217, b"GET /C:/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/C:/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_218, b"GET /C:/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/C:/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_219, b"GET /dir/C HTTP/1.1\r\nHost: host\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/dir/C"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"host"); } } req! { urltest_220, b"GET /dir/C|a HTTP/1.1\r\nHost: host\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/dir/C|a"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"host"); } } req! { urltest_221, b"GET /c:/foo/bar HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/c:/foo/bar"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_222, b"GET /c:/foo/bar HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/c:/foo/bar"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_223, b"GET /c:/foo/bar HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/c:/foo/bar"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_224, b"GET /c:/foo/bar HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/c:/foo/bar"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_225, b"GET /C:/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/C:/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_226, b"GET /C:/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/C:/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_227, b"GET /C:/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/C:/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_228, b"GET /C:/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/C:/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_229, b"GET /C:/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/C:/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_230, b"GET /?q=v HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/?q=v"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_231, b"GET ?x HTTP/1.1\r\nHost: %C3%B1\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "?x"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"%C3%B1"); } } req! { urltest_232, b"GET ?x HTTP/1.1\r\nHost: %C3%B1\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "?x"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"%C3%B1"); } } req! { urltest_233, b"GET // HTTP/1.1\r\nHost: \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(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_234, b"GET //x/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "//x/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_235, b"GET /someconfig;mode=netascii HTTP/1.1\r\nHost: foobar.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/someconfig;mode=netascii"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"foobar.com"); } } req! { urltest_236, b"GET /Index.ut2 HTTP/1.1\r\nHost: 10.10.10.10\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/Index.ut2"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"10.10.10.10"); } } req! { urltest_237, b"GET /0?baz=bam&qux=baz HTTP/1.1\r\nHost: somehost\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/0?baz=bam&qux=baz"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"somehost"); } } req! { urltest_238, b"GET /sup HTTP/1.1\r\nHost: host\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/sup"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"host"); } } req! { urltest_239, b"GET /foo/bar.git HTTP/1.1\r\nHost: github.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/bar.git"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"github.com"); } } req! { urltest_240, b"GET /channel?passwd HTTP/1.1\r\nHost: myserver.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/channel?passwd"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"myserver.com"); } } req! { urltest_241, b"GET /foo.bar.org?type=TXT HTTP/1.1\r\nHost: fw.example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo.bar.org?type=TXT"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"fw.example.org"); } } req! { urltest_242, b"GET /ou=People,o=JNDITutorial HTTP/1.1\r\nHost: localhost\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/ou=People,o=JNDITutorial"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"localhost"); } } req! { urltest_243, b"GET /foo/bar HTTP/1.1\r\nHost: github.com\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/foo/bar"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"github.com"); } } req! { urltest_244, b"GET ietf:rfc:2648 HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "ietf:rfc:2648"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_245, b"GET joe@example.org,2001:foo/bar HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "joe@example.org,2001:foo/bar"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_246, b"GET /path HTTP/1.1\r\nHost: H%4fSt\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/path"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"H%4fSt"); } } req! { urltest_247, b"GET https://example.com:443/ HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "https://example.com:443/"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_248, b"GET d3958f5c-0777-0845-9dcf-2cb28783acaf HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "d3958f5c-0777-0845-9dcf-2cb28783acaf"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_249, b"GET /test?%22 HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/test?%22"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_250, b"GET /test HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/test"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_251, b"GET /test?%3C HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/test?%3C"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_252, b"GET /test?%3E HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/test?%3E"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_253, b"GET /test?%E2%8C%A3 HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/test?%E2%8C%A3"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_254, b"GET /test?%23%23 HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/test?%23%23"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_255, b"GET /test?%GH HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/test?%GH"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_256, b"GET /test?a HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/test?a"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_257, b"GET /test?a HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/test?a"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } req! { urltest_258, b"GET /test-a-colon-slash.html HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/test-a-colon-slash.html"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_259, b"GET /test-a-colon-slash-slash.html HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/test-a-colon-slash-slash.html"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_260, b"GET /test-a-colon-slash-b.html HTTP/1.1\r\nHost: \r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/test-a-colon-slash-b.html"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b""); } } req! { urltest_261, b"GET /test-a-colon-slash-slash-b.html HTTP/1.1\r\nHost: b\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/test-a-colon-slash-slash-b.html"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"b"); } } req! { urltest_262, b"GET /test?a HTTP/1.1\r\nHost: example.org\r\n\r\n", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/test?a"); assert_eq!(req.version.unwrap(), 1); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"example.org"); } } 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", |req| { assert_eq!(req.method.unwrap(), "GET"); assert_eq!(req.path.unwrap(), "/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\"}"); assert_eq!(req.version.unwrap(), 0); assert_eq!(req.headers.len(), 1); assert_eq!(req.headers[0].name, "Host"); assert_eq!(req.headers[0].value, b"gfwsl.geforce.com"); } }