1use crate::{CanonicalRequest, Method, Protocol, Transport};
38
39#[derive(Debug, Clone, Copy)]
41pub struct NormalizeConfig {
42 pub decode_path: bool,
44 pub merge_headers: bool,
46 pub normalize_case: bool,
48}
49
50impl Default for NormalizeConfig {
51 fn default() -> Self {
52 Self {
53 decode_path: true,
54 merge_headers: true,
55 normalize_case: true,
56 }
57 }
58}
59
60#[derive(Debug, Clone)]
62pub struct NormalizeError {
63 pub message: &'static str,
65}
66
67impl core::fmt::Display for NormalizeError {
68 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
69 write!(f, "NormalizeError: {}", self.message)
70 }
71}
72
73#[allow(clippy::too_many_arguments)]
92pub fn normalize_request(
93 method: Method,
94 scheme: &str,
95 authority: &str,
96 path: &str,
97 query: &str,
98 headers: &[(&str, &str)],
99 protocol: Protocol,
100 transport: Transport,
101) -> Result<CanonicalRequest, NormalizeError> {
102 let config = NormalizeConfig::default();
103 normalize_request_with_config(
104 method, scheme, authority, path, query, headers, protocol, transport, config,
105 )
106}
107
108#[allow(clippy::too_many_arguments)]
110pub fn normalize_request_with_config(
111 method: Method,
112 scheme: &str,
113 authority: &str,
114 path: &str,
115 query: &str,
116 headers: &[(&str, &str)],
117 protocol: Protocol,
118 transport: Transport,
119 config: NormalizeConfig,
120) -> Result<CanonicalRequest, NormalizeError> {
121 let mut request = CanonicalRequest::empty();
122
123 request.method = method;
125 request.protocol = protocol;
126 request.transport = transport;
127
128 let normalized_scheme = if config.normalize_case {
130 scheme.to_lowercase()
131 } else {
132 scheme.to_string()
133 };
134 if !request.set_scheme(&normalized_scheme) {
136 return Err(NormalizeError {
137 message: "scheme too long",
138 });
139 }
140
141 let normalized_authority = normalize_authority(authority, &normalized_scheme);
143 if !request.set_authority(&normalized_authority) {
144 return Err(NormalizeError {
145 message: "authority too long",
146 });
147 }
148
149 let normalized_path = normalize_path(path, config.decode_path)?;
151 if !request.set_path(&normalized_path) {
152 return Err(NormalizeError {
153 message: "path too long",
154 });
155 }
156
157 let normalized_query = normalize_query(query, config.decode_path);
159 if !request.set_query(&normalized_query) {
160 return Err(NormalizeError {
161 message: "query too long",
162 });
163 }
164
165 normalize_headers(&mut request, headers, config)?;
167
168 Ok(request)
169}
170
171pub fn normalize_path(path: &str, decode: bool) -> Result<String, NormalizeError> {
178 if path.is_empty() {
179 return Ok("/".to_string());
180 }
181
182 let bytes = path.as_bytes();
183 let mut result = Vec::with_capacity(path.len());
184
185 let has_trailing_slash = bytes.ends_with(b"/");
188
189 let starts_with_slash = bytes[0] == b'/';
191 if starts_with_slash {
192 result.push(b'/');
193 }
194
195 let segments: Vec<&[u8]> = bytes
196 .split(|&b| b == b'/')
197 .filter(|s| !s.is_empty())
198 .collect();
199
200 let mut stack: Vec<Vec<u8>> = Vec::with_capacity(segments.len());
205
206 for segment in &segments {
207 let decoded: Vec<u8> = if decode {
208 safe_percent_decode(segment)
211 } else {
212 segment.to_vec()
213 };
214 match decoded.as_slice() {
215 b"." => {
216 }
218 b".." => {
219 stack.pop();
221 }
222 _ => {
223 stack.push(decoded);
224 }
225 }
226 }
227
228 for (i, segment) in stack.iter().enumerate() {
229 if i > 0 {
231 result.push(b'/');
232 }
233 result.extend_from_slice(segment);
234 }
235
236 if result.is_empty() {
238 result.push(b'/');
239 }
240
241 if has_trailing_slash && !result.ends_with(b"/") {
245 result.push(b'/');
246 }
247
248 String::from_utf8(result).map_err(|_| NormalizeError {
250 message: "invalid UTF-8 in path",
251 })
252}
253
254pub fn normalize_query(query: &str, decode: bool) -> String {
265 if query.is_empty() {
266 return String::new();
267 }
268
269 let trimmed = query.trim_start_matches('?');
271
272 let normalized: String = trimmed
274 .chars()
275 .map(|c| if c.is_whitespace() { ' ' } else { c })
276 .collect();
277
278 if !decode {
279 return normalized;
280 }
281
282 match String::from_utf8(safe_percent_decode(normalized.as_bytes())) {
288 Ok(decoded) => decoded,
289 Err(_) => normalized,
290 }
291}
292
293pub fn normalize_authority(authority: &str, scheme: &str) -> String {
310 if authority.is_empty() {
311 return String::new();
312 }
313
314 let (host, port) = if let Some(bracket_start) = authority.find('[') {
315 match authority.find(']') {
319 Some(bracket_end) if bracket_end > bracket_start => {
320 let host = &authority[bracket_start..=bracket_end];
321 let port_part = authority.get(bracket_end + 1..).unwrap_or("");
322 let port = port_part.trim_start_matches(':');
323 if port.is_empty() || port.parse::<u16>().is_ok() {
326 (host.to_string(), port.to_string())
327 } else {
328 (host.to_string(), String::new())
329 }
330 }
331 _ => (authority.to_string(), String::new()),
332 }
333 } else if let Some(colon_pos) = authority.rfind(':') {
334 let host = &authority[..colon_pos];
336 let port = &authority[colon_pos + 1..];
337 if port.parse::<u16>().is_ok() {
339 (host.to_string(), port.to_string())
340 } else {
341 (authority.to_string(), String::new())
342 }
343 } else {
344 (authority.to_string(), String::new())
345 };
346
347 let normalized_host = host.to_lowercase();
348
349 let default_port = match scheme {
351 "http" => Some("80"),
352 "https" => Some("443"),
353 _ => None,
354 };
355
356 if !port.is_empty() {
357 if let Some(default) = default_port
358 && port == default
359 {
360 return normalized_host;
362 }
363 format!("{}:{}", normalized_host, port)
364 } else {
365 normalized_host
366 }
367}
368
369pub fn normalize_host_key(host: &str) -> String {
391 let h = host.trim();
392 if h.is_empty() {
393 return String::new();
394 }
395 let (name, port) = split_host_port(h);
396 let mut name = name.to_lowercase();
397 while name.ends_with('.') {
399 name.pop();
400 }
401 match port {
403 Some("80") | Some("443") => name,
404 Some(p) => format!("{name}:{p}"),
405 None => name,
406 }
407}
408
409pub fn split_host_port(h: &str) -> (&str, Option<&str>) {
421 if let Some(end) = h.strip_prefix('[').and_then(|s| s.find(']')) {
422 let addr = &h[..=end + 1]; let rest = &h[end + 2..];
425 match rest.strip_prefix(':') {
426 Some(p) if !p.is_empty() => (addr, Some(p)),
427 _ => (addr, None),
428 }
429 } else if let Some(idx) = h.rfind(':') {
430 let (name, p) = h.split_at(idx);
432 let p = &p[1..];
433 if !p.is_empty() && p.bytes().all(|b| b.is_ascii_digit()) {
434 (name, Some(p))
435 } else {
436 (h, None)
437 }
438 } else {
439 (h, None)
440 }
441}
442
443#[inline]
454pub fn unbracket_ipv6(h: &str) -> &str {
455 h.strip_prefix('[')
456 .and_then(|s| s.strip_suffix(']'))
457 .unwrap_or(h)
458}
459
460pub fn normalize_headers(
467 request: &mut CanonicalRequest,
468 headers: &[(&str, &str)],
469 config: NormalizeConfig,
470) -> Result<(), NormalizeError> {
471 if headers.is_empty() {
472 return Ok(());
473 }
474
475 if config.merge_headers {
477 let mut merged: Vec<(String, Vec<String>)> = Vec::new();
478
479 for (name, value) in headers {
480 if name.starts_with(':') {
482 continue;
483 }
484
485 let normalized_name = if config.normalize_case {
486 name.to_lowercase()
487 } else {
488 name.to_string()
489 };
490
491 if !is_valid_header_name(&normalized_name) {
493 return Err(NormalizeError {
494 message: "invalid header name",
495 });
496 }
497
498 let normalized_value = value.trim().to_string();
500
501 if let Some(entry) = merged.iter_mut().find(|(n, _)| n == &normalized_name) {
503 entry.1.push(normalized_value);
504 } else {
505 merged.push((normalized_name, vec![normalized_value]));
506 }
507 }
508
509 for (name, values) in &merged {
511 let combined_value = values.join(", ");
512 if combined_value.len() > crate::MAX_HEADER_VALUE_LEN {
516 return Err(NormalizeError {
517 message: "header value too long",
518 });
519 }
520 request
521 .add_header(name.as_bytes(), combined_value.as_bytes())
522 .map_err(|msg| NormalizeError { message: msg })?;
523 }
524 } else {
525 for (name, value) in headers {
527 if name.starts_with(':') {
529 continue;
530 }
531
532 let normalized_name = if config.normalize_case {
533 name.to_lowercase()
534 } else {
535 name.to_string()
536 };
537
538 if !is_valid_header_name(&normalized_name) {
539 return Err(NormalizeError {
540 message: "invalid header name",
541 });
542 }
543
544 let normalized_value = value.trim();
545 request
546 .add_header(normalized_name.as_bytes(), normalized_value.as_bytes())
547 .map_err(|msg| NormalizeError { message: msg })?;
548 }
549 }
550
551 Ok(())
552}
553
554pub fn is_valid_header_name(name: &str) -> bool {
560 if name.is_empty() {
561 return false;
562 }
563 name.chars().all(|c| {
564 c.is_ascii_alphanumeric()
565 || matches!(
566 c,
567 '!' | '#' | '$' | '%' | '&' | '\'' | '*' | '+' | '-' | '.' | '^' | '_' | '`' | '|' | '~'
568 )
569 })
570}
571
572#[derive(Debug, Clone, Copy, PartialEq, Eq)]
577pub enum InvalidSequencePolicy {
578 Reject,
581 Preserve,
584}
585
586pub fn percent_decode(input: &str, plus_as_space: bool) -> Option<String> {
598 percent_decode_with_policy(input, plus_as_space, InvalidSequencePolicy::Reject)
599}
600
601pub fn percent_decode_with_policy(
617 input: &str,
618 plus_as_space: bool,
619 policy: InvalidSequencePolicy,
620) -> Option<String> {
621 let mut out = Vec::with_capacity(input.len());
622 decode_percent_vec(input.as_bytes(), plus_as_space, policy, &mut out)?;
623 String::from_utf8(out).ok()
625}
626
627pub fn percent_decode_bytes(input: &[u8], plus_as_space: bool) -> Vec<u8> {
635 let mut out = Vec::with_capacity(input.len());
636 let _ = decode_percent_vec(input, plus_as_space, InvalidSequencePolicy::Preserve, &mut out);
638 out
639}
640
641pub fn percent_decode_bytes_into<'a>(
651 input: &[u8],
652 buf: &'a mut [u8],
653 plus_as_space: bool,
654) -> Option<&'a [u8]> {
655 let mut i = 0usize;
656 let mut j = 0usize;
657
658 while i < input.len() {
659 if j >= buf.len() {
661 return None;
662 }
663 match input[i] {
664 b'%' => {
665 let hi = input.get(i + 1).copied().and_then(hex_value);
667 let lo = input.get(i + 2).copied().and_then(hex_value);
668 match (hi, lo) {
669 (Some(h), Some(l)) => {
670 buf[j] = (h << 4) | l;
672 j += 1;
673 i += 3;
674 }
675 _ => {
677 buf[j] = b'%';
678 j += 1;
679 i += 1;
680 }
681 }
682 }
683 b'+' if plus_as_space => {
684 buf[j] = b' ';
685 j += 1;
686 i += 1;
687 }
688 b => {
689 buf[j] = b;
690 j += 1;
691 i += 1;
692 }
693 }
694 }
695
696 Some(&buf[..j])
697}
698
699fn decode_percent_vec(
704 input: &[u8],
705 plus_as_space: bool,
706 policy: InvalidSequencePolicy,
707 out: &mut Vec<u8>,
708) -> Option<()> {
709 let mut i = 0usize;
710
711 while i < input.len() {
712 match input[i] {
713 b'%' => {
714 let hi = input.get(i + 1).copied().and_then(hex_value);
716 let lo = input.get(i + 2).copied().and_then(hex_value);
717 match (hi, lo) {
718 (Some(h), Some(l)) => {
719 out.push((h << 4) | l);
721 i += 3;
722 }
723 _ => match policy {
724 InvalidSequencePolicy::Reject => return None,
726 InvalidSequencePolicy::Preserve => {
728 out.push(b'%');
729 i += 1;
730 }
731 },
732 }
733 }
734 b'+' if plus_as_space => {
735 out.push(b' ');
736 i += 1;
737 }
738 b => {
739 out.push(b);
740 i += 1;
741 }
742 }
743 }
744
745 Some(())
746}
747
748const fn hex_value(b: u8) -> Option<u8> {
750 match b {
751 b'0'..=b'9' => Some(b - b'0'),
752 b'a'..=b'f' => Some(b - b'a' + 10),
753 b'A'..=b'F' => Some(b - b'A' + 10),
754 _ => None,
755 }
756}
757
758fn safe_percent_decode(input: &[u8]) -> Vec<u8> {
763 let mut result = Vec::with_capacity(input.len());
764 let mut i = 0;
765
766 while i < input.len() {
767 if input[i] == b'%' && i + 2 < input.len() {
768 let hex = &input[i + 1..i + 3];
770 if let Ok(byte) = u8::from_str_radix(
771 core::str::from_utf8(hex).unwrap_or(""),
772 16,
773 ) {
774 if is_unreserved(byte) {
776 result.push(byte);
777 i += 3;
778 continue;
779 }
780 }
781 }
782 result.push(input[i]);
783 i += 1;
784 }
785
786 result
787}
788
789fn is_unreserved(b: u8) -> bool {
798 b.is_ascii_alphanumeric() || matches!(b, b'-' | b'_' | b'.' | b'~' | b'%')
799}
800
801#[cfg(test)]
802mod tests {
803 use super::*;
804
805 #[test]
809 fn split_host_port_and_unbracket_ipv6_matrix() {
810 assert_eq!(split_host_port("example.com:8443"), ("example.com", Some("8443")));
812 assert_eq!(split_host_port("example.com"), ("example.com", None));
813 assert_eq!(split_host_port("example.com:abc"), ("example.com:abc", None));
815 assert_eq!(split_host_port("[::1]"), ("[::1]", None));
817 assert_eq!(split_host_port("[::1]:8443"), ("[::1]", Some("8443")));
818 assert_eq!(split_host_port("[fe80::ff]:443"), ("[fe80::ff]", Some("443")));
820 assert_eq!(split_host_port("[::ffff:1.2.3.4]"), ("[::ffff:1.2.3.4]", None));
821
822 assert_eq!(unbracket_ipv6("[::1]"), "::1");
824 assert_eq!(unbracket_ipv6("[a]"), "a");
825 assert_eq!(unbracket_ipv6("::1"), "::1");
826 assert_eq!(unbracket_ipv6("example.com"), "example.com");
827 assert_eq!(unbracket_ipv6("[::"), "[::");
828 assert_eq!(unbracket_ipv6("a]"), "a]");
829 assert_eq!(unbracket_ipv6(""), "");
830 }
831
832 #[test]
833 fn test_normalize_path_simple() {
834 let result = normalize_path("/simple/path", true).unwrap();
835 assert_eq!(result, "/simple/path");
836 }
837
838 #[test]
839 fn test_normalize_path_dot_segments() {
840 let result = normalize_path("/a/./b/../c", true).unwrap();
841 assert_eq!(result, "/a/c");
842 }
843
844 #[test]
845 fn test_normalize_path_double_dot() {
846 let result = normalize_path("/a/b/c/../../d", true).unwrap();
847 assert_eq!(result, "/a/d");
848 }
849
850 #[test]
851 fn test_normalize_path_root() {
852 let result = normalize_path("/", true).unwrap();
853 assert_eq!(result, "/");
854 }
855
856 #[test]
857 fn test_normalize_path_empty() {
858 let result = normalize_path("", true).unwrap();
859 assert_eq!(result, "/");
860 }
861
862 #[test]
863 fn test_normalize_path_multiple_slashes() {
864 let result = normalize_path("//a///b", true).unwrap();
865 assert_eq!(result, "/a/b");
866 }
867
868 #[test]
869 fn test_normalize_path_percent_decode() {
870 let result = normalize_path("/hello%7Eworld", true).unwrap();
872 assert_eq!(result, "/hello~world");
873 }
874
875 #[test]
876 fn test_normalize_path_no_decode() {
877 let result = normalize_path("/hello%20world", false).unwrap();
878 assert_eq!(result, "/hello%20world");
879 }
880
881 #[test]
886 fn test_normalize_path_encoded_dot_segments_folded() {
887 assert_eq!(normalize_path("/a/%2e%2e/b", true).unwrap(), "/b");
889 assert_eq!(normalize_path("/a/%2e/b", true).unwrap(), "/a/b");
890 assert_eq!(
892 normalize_path("/%2e%2e/etc/passwd", true).unwrap(),
893 "/etc/passwd"
894 );
895 assert_eq!(
897 normalize_path("/a/%252e%252e/b", true).unwrap(),
898 "/a/%2e%2e/b"
899 );
900 }
901
902 #[test]
903 fn test_normalize_path_encoded_dot_segments_mixed_case() {
904 assert_eq!(normalize_path("/a/%2E%2e/b", true).unwrap(), "/b");
906 assert_eq!(normalize_path("/a/%2e%2E/b", true).unwrap(), "/b");
907 assert_eq!(normalize_path("/a/%2E/b", true).unwrap(), "/a/b");
908 }
909
910 #[test]
911 fn test_normalize_path_encoded_dot_segments_reserved_kept() {
912 assert_eq!(
914 normalize_path("/a%2f..%2fb", true).unwrap(),
915 "/a%2f..%2fb"
916 );
917 assert_eq!(normalize_path("/a/..%2e/b", true).unwrap(), "/a/.../b");
919 }
920
921 #[test]
922 fn test_normalize_path_encoded_dot_segments_no_decode() {
923 assert_eq!(
925 normalize_path("/a/%2e%2e/b", false).unwrap(),
926 "/a/%2e%2e/b"
927 );
928 assert_eq!(
929 normalize_path("/%2e%2e/etc/passwd", false).unwrap(),
930 "/%2e%2e/etc/passwd"
931 );
932 }
933
934 #[test]
935 fn test_normalize_query() {
936 let result = normalize_query("key=value&foo=bar", true);
937 assert_eq!(result, "key=value&foo=bar");
938 }
939
940 #[test]
941 fn test_normalize_query_with_leading_question() {
942 let result = normalize_query("?key=value", true);
943 assert_eq!(result, "key=value");
944 }
945
946 #[test]
947 fn test_normalize_query_empty() {
948 let result = normalize_query("", true);
949 assert!(result.is_empty());
950 }
951
952 #[test]
953 fn test_normalize_host_key_cache_semantics() {
954 assert_eq!(normalize_host_key(" ExAmPLE.com "), "example.com");
956 assert_eq!(normalize_host_key("example.com."), "example.com");
958 assert_eq!(normalize_host_key("example.com.."), "example.com");
959 assert_eq!(normalize_host_key("example.com:80"), "example.com");
961 assert_eq!(normalize_host_key("example.com:443"), "example.com");
962 assert_eq!(normalize_host_key("example.com:8080"), "example.com:8080");
964 assert_eq!(normalize_host_key("[::1]:8080"), "[::1]:8080");
966 assert_eq!(normalize_host_key("[::1]:443"), "[::1]");
967 assert_eq!(normalize_host_key("[2001:db8::a]"), "[2001:db8::a]");
968 assert_eq!(normalize_host_key("example.com:abc"), "example.com:abc");
970 assert_eq!(normalize_host_key(""), "");
972 }
973
974 #[test]
975 fn test_normalize_authority_reverse_brackets_no_panic() {
976 let _ = normalize_authority("]x[", "https");
979 let _ = normalize_authority("]:80[abc", "http");
980 let _ = normalize_authority("[]", "https");
981 assert_eq!(normalize_authority("[::1]:4444", "https"), "[::1]:4444");
983 assert_eq!(normalize_authority("[::1]:443", "https"), "[::1]");
984 }
985
986 #[test]
987 fn test_normalize_authority() {
988 let result = normalize_authority("Example.COM:443", "https");
989 assert_eq!(result, "example.com");
990 }
991
992 #[test]
993 fn test_normalize_authority_non_default_port() {
994 let result = normalize_authority("example.com:8080", "http");
995 assert_eq!(result, "example.com:8080");
996 }
997
998 #[test]
999 fn test_normalize_authority_http_default() {
1000 let result = normalize_authority("example.com:80", "http");
1001 assert_eq!(result, "example.com");
1002 }
1003
1004 #[test]
1005 fn test_normalize_authority_ipv6() {
1006 let result = normalize_authority("[::1]:8080", "http");
1007 assert_eq!(result, "[::1]:8080");
1008 }
1009
1010 #[test]
1011 fn test_normalize_headers_basic() {
1012 let headers = [("Content-Type", "application/json"), ("Accept", "text/html")];
1013 let result = normalize_request(
1014 Method::Get,
1015 "https",
1016 "example.com",
1017 "/test",
1018 "",
1019 &headers,
1020 Protocol::Http1,
1021 Transport::Tls13,
1022 )
1023 .unwrap();
1024
1025 assert_eq!(result.find_header("content-type").unwrap().value_str(), "application/json");
1026 assert_eq!(result.find_header("accept").unwrap().value_str(), "text/html");
1027 }
1028
1029 #[test]
1030 fn test_normalize_headers_merge() {
1031 let headers = [
1032 ("X-Custom", "value1"),
1033 ("x-custom", "value2"),
1034 ("Accept", "text/html"),
1035 ];
1036 let result = normalize_request(
1037 Method::Get,
1038 "https",
1039 "example.com",
1040 "/test",
1041 "",
1042 &headers,
1043 Protocol::Http1,
1044 Transport::Tls13,
1045 )
1046 .unwrap();
1047
1048 assert_eq!(result.header_count(), 2);
1050 let custom = result.find_header("x-custom").unwrap();
1052 assert!(custom.value_str().contains("value1"));
1053 assert!(custom.value_str().contains("value2"));
1054 }
1055
1056 #[test]
1057 fn test_normalize_headers_merged_value_overlong_precise_error() {
1058 let v100 = "a".repeat(100);
1062 let headers = [
1063 ("X-Big", v100.as_str()),
1064 ("x-big", v100.as_str()),
1065 ("X-BIG", v100.as_str()),
1066 ];
1067 let result = normalize_request(
1068 Method::Get,
1069 "http",
1070 "example.com",
1071 "/",
1072 "",
1073 &headers,
1074 Protocol::Http1,
1075 Transport::Plaintext,
1076 );
1077 let err = result.expect_err("合并总长超限必须报错");
1078 assert_eq!(
1079 err.message, "header value too long",
1080 "合并超长必须返回精确错误而非 header count exceeded"
1081 );
1082 }
1083
1084 #[test]
1085 fn test_normalize_headers_invalid_name() {
1086 let headers = [("Invalid Header!", "value"), ("Valid", "ok")];
1087 let result = normalize_request(
1088 Method::Get,
1089 "https",
1090 "example.com",
1091 "/test",
1092 "",
1093 &headers,
1094 Protocol::Http1,
1095 Transport::Tls13,
1096 );
1097
1098 let err = result.expect_err("invalid header name should be rejected");
1100 assert_eq!(err.message, "invalid header name");
1101 }
1102
1103 #[test]
1104 fn test_is_valid_header_name() {
1105 assert!(is_valid_header_name("content-type"));
1106 assert!(is_valid_header_name("x-custom-header"));
1107 assert!(is_valid_header_name("Authorization"));
1108 assert!(!is_valid_header_name("invalid header"));
1109 assert!(!is_valid_header_name("header\r\n"));
1110 }
1111
1112 #[test]
1113 fn test_full_normalize() {
1114 let headers = [
1115 ("Content-Type", "application/json"),
1116 ("Host", "Example.COM"),
1117 ];
1118 let result = normalize_request(
1119 Method::Post,
1120 "HTTPS",
1121 "Example.COM:443",
1122 "/Api/Test/./..",
1123 "?key=value",
1124 &headers,
1125 Protocol::Http2,
1126 Transport::Tls13,
1127 )
1128 .unwrap();
1129
1130 assert_eq!(result.method, Method::Post);
1131 assert_eq!(result.scheme_str(), "https");
1132 assert_eq!(result.authority_str(), "example.com");
1133 assert_eq!(result.path_str(), "/Api");
1135 assert_eq!(result.query_str(), "key=value");
1136 assert_eq!(result.find_header("content-type").unwrap().value_str(), "application/json");
1137 assert_eq!(result.find_header("host").unwrap().value_str(), "Example.COM");
1138 }
1139
1140 #[test]
1141 fn test_safe_percent_decode() {
1142 let result = safe_percent_decode(b"hello%7Eworld");
1143 assert_eq!(result, b"hello~world");
1144
1145 let result = safe_percent_decode(b"test%2Fpath");
1147 assert_eq!(result, b"test%2Fpath"); }
1149
1150 #[test]
1155 fn test_normalize_path_trailing_slash() {
1156 let result = normalize_path("/a/b/", true).unwrap();
1158 assert_eq!(result, "/a/b/");
1159 }
1160
1161 #[test]
1162 fn test_normalize_path_only_dots() {
1163 let result = normalize_path("/../../../..", true).unwrap();
1165 assert_eq!(result, "/");
1166 }
1167
1168 #[test]
1169 fn test_normalize_path_single_dot_root() {
1170 let result = normalize_path("/.", true).unwrap();
1171 assert_eq!(result, "/");
1172 }
1173
1174 #[test]
1175 fn test_normalize_path_double_dot_root() {
1176 let result = normalize_path("/..", true).unwrap();
1177 assert_eq!(result, "/");
1178 }
1179
1180 #[test]
1181 fn test_normalize_path_complex_dots() {
1182 let result = normalize_path("/a/b/c/./d/../e/../../f", true).unwrap();
1183 assert_eq!(result, "/a/b/f");
1184 }
1185
1186 #[test]
1187 fn test_normalize_path_multiple_dots_in_segment() {
1188 let result = normalize_path("/..hidden/.bashrc/test..", true).unwrap();
1190 assert_eq!(result, "/..hidden/.bashrc/test..");
1191 }
1192
1193 #[test]
1194 fn test_normalize_path_no_leading_slash() {
1195 let result = normalize_path("a/b/c", true).unwrap();
1196 assert_eq!(result, "a/b/c");
1197 }
1198
1199 #[test]
1200 fn test_normalize_path_single_segment() {
1201 let result = normalize_path("/test", true).unwrap();
1202 assert_eq!(result, "/test");
1203 }
1204
1205 #[test]
1206 fn test_normalize_path_only_slashes() {
1207 let result = normalize_path("///", true).unwrap();
1208 assert_eq!(result, "/");
1209 }
1210
1211 #[test]
1212 fn test_normalize_path_many_slashes() {
1213 let result = normalize_path("/a///b//c////d", true).unwrap();
1214 assert_eq!(result, "/a/b/c/d");
1215 }
1216
1217 #[test]
1222 fn test_percent_decode_mixed_case_hex() {
1223 let result = safe_percent_decode(b"test%2fdata");
1225 assert_eq!(result, b"test%2fdata"); let result = safe_percent_decode(b"hello%7eworld");
1228 assert_eq!(result, b"hello~world"); }
1230
1231 #[test]
1232 fn test_percent_decode_uppercase_hex() {
1233 let result = safe_percent_decode(b"hello%7Eworld");
1234 assert_eq!(result, b"hello~world");
1235 }
1236
1237 #[test]
1238 fn test_percent_decode_incomplete_sequence() {
1239 let result = safe_percent_decode(b"test%2");
1241 assert_eq!(result, b"test%2");
1242
1243 let result = safe_percent_decode(b"test%");
1244 assert_eq!(result, b"test%");
1245 }
1246
1247 #[test]
1248 fn test_percent_decode_invalid_hex() {
1249 let result = safe_percent_decode(b"test%ZZdata");
1251 assert_eq!(result, b"test%ZZdata");
1252 }
1253
1254 #[test]
1255 fn test_percent_decode_unreserved_chars() {
1256 let result = safe_percent_decode(b"%41%5A%61%7A%30%39%2D%5F%2E%7E");
1259 assert_eq!(result, b"AZaz09-_.~");
1260 }
1261
1262 #[test]
1263 fn test_percent_decode_reserved_chars_kept() {
1264 let result = safe_percent_decode(b"%2F%3F%23%5B%5D%40%21%24%26%27%28%29%2A%2B%2C%3B%3D");
1266 assert_eq!(result, b"%2F%3F%23%5B%5D%40%21%24%26%27%28%29%2A%2B%2C%3B%3D");
1267 }
1268
1269 #[test]
1270 fn test_percent_decode_multiple_sequences() {
1271 let result = safe_percent_decode(b"%7E%7Ehello%7Eworld%7E%7E");
1272 assert_eq!(result, b"~~hello~world~~");
1273 }
1274
1275 #[test]
1276 fn test_percent_decode_empty_input() {
1277 let result = safe_percent_decode(b"");
1278 assert_eq!(result, b"");
1279 }
1280
1281 #[test]
1286 fn test_percent_decode_pub_basic() {
1287 assert_eq!(percent_decode("hello%20world", false).unwrap(), "hello world");
1288 assert_eq!(percent_decode("%41%42%43", false).unwrap(), "ABC");
1289 assert_eq!(percent_decode("plain", false).unwrap(), "plain");
1290 assert_eq!(percent_decode("", false).unwrap(), "");
1291 }
1292
1293 #[test]
1294 fn test_percent_decode_pub_plus_as_space() {
1295 assert_eq!(percent_decode("a+b+c", true).unwrap(), "a b c");
1297 assert_eq!(percent_decode("a+b", false).unwrap(), "a+b");
1299 assert_eq!(percent_decode("a%2Bb", true).unwrap(), "a+b");
1301 }
1302
1303 #[test]
1304 fn test_percent_decode_pub_invalid_sequences() {
1305 assert!(percent_decode("test%", false).is_none());
1307 assert!(percent_decode("test%2", false).is_none());
1308 assert!(percent_decode("test%ZZdata", false).is_none());
1310 assert!(percent_decode("%2g", false).is_none());
1311 assert!(percent_decode("%g2", false).is_none());
1312 assert!(percent_decode("%FF%FE", false).is_none());
1314 }
1315
1316 #[test]
1317 fn test_percent_decode_pub_mixed_case_hex() {
1318 assert_eq!(percent_decode("%7e", false).unwrap(), "~");
1319 assert_eq!(percent_decode("%7E", false).unwrap(), "~");
1320 assert_eq!(percent_decode("%2f", false).unwrap(), "/");
1321 assert_eq!(percent_decode("%2F", false).unwrap(), "/");
1322 }
1323
1324 #[test]
1329 fn test_policy_reject_delegates_percent_decode() {
1330 let cases = [
1332 "hello%20world",
1333 "a+b",
1334 "test%",
1335 "test%2",
1336 "test%ZZdata",
1337 "%FF%FE",
1338 "%41%42%43",
1339 "",
1340 ];
1341 for case in cases {
1342 for plus in [false, true] {
1343 assert_eq!(
1344 percent_decode_with_policy(case, plus, InvalidSequencePolicy::Reject),
1345 percent_decode(case, plus),
1346 "Reject 策略与 percent_decode 不一致: {:?} (plus={})",
1347 case,
1348 plus
1349 );
1350 }
1351 }
1352 }
1353
1354 #[test]
1355 fn test_policy_preserve_invalid_sequences() {
1356 assert_eq!(
1358 percent_decode_with_policy("test%ZZdata", false, InvalidSequencePolicy::Preserve).unwrap(),
1359 "test%ZZdata"
1360 );
1361 assert_eq!(
1362 percent_decode_with_policy("%", false, InvalidSequencePolicy::Preserve).unwrap(),
1363 "%"
1364 );
1365 assert_eq!(
1366 percent_decode_with_policy("%2", false, InvalidSequencePolicy::Preserve).unwrap(),
1367 "%2"
1368 );
1369 assert_eq!(
1371 percent_decode_with_policy("100%+pure", true, InvalidSequencePolicy::Preserve).unwrap(),
1372 "100% pure"
1373 );
1374 assert_eq!(
1376 percent_decode_with_policy("%2Z%41", false, InvalidSequencePolicy::Preserve).unwrap(),
1377 "%2ZA"
1378 );
1379 }
1380
1381 #[test]
1382 fn test_policy_preserve_invalid_utf8_rejected() {
1383 assert!(percent_decode_with_policy("%FF", false, InvalidSequencePolicy::Preserve).is_none());
1385 assert!(percent_decode_with_policy("%FF%FE", true, InvalidSequencePolicy::Preserve).is_none());
1386 }
1387
1388 #[test]
1389 fn test_policy_preserve_plus_as_space() {
1390 assert_eq!(
1391 percent_decode_with_policy("a+b+c", true, InvalidSequencePolicy::Preserve).unwrap(),
1392 "a b c"
1393 );
1394 assert_eq!(
1395 percent_decode_with_policy("a+b", false, InvalidSequencePolicy::Preserve).unwrap(),
1396 "a+b"
1397 );
1398 }
1399
1400 #[test]
1405 fn test_percent_decode_bytes_basic() {
1406 assert_eq!(percent_decode_bytes(b"hello%20world", false), b"hello world");
1407 assert_eq!(percent_decode_bytes(b"a+b", true), b"a b");
1408 assert_eq!(percent_decode_bytes(b"a+b", false), b"a+b");
1409 assert_eq!(percent_decode_bytes(b"%ZZ", false), b"%ZZ");
1411 assert_eq!(percent_decode_bytes(b"%", false), b"%");
1412 assert_eq!(percent_decode_bytes(b"%2", false), b"%2");
1413 assert_eq!(percent_decode_bytes(b"%FF", false), b"\xFF");
1415 assert_eq!(percent_decode_bytes(b"", false), b"");
1416 }
1417
1418 #[test]
1419 fn test_percent_decode_bytes_into_basic() {
1420 let mut buf = [0u8; 64];
1421 assert_eq!(
1422 percent_decode_bytes_into(b"id=%27+OR+%271%27%3D%271", &mut buf, true),
1423 Some(&b"id=' OR '1'='1"[..])
1424 );
1425 assert_eq!(
1427 percent_decode_bytes_into(b"%FF", &mut buf, true),
1428 Some(&b"\xFF"[..])
1429 );
1430 assert_eq!(
1432 percent_decode_bytes_into(b"%ZZ", &mut buf, true),
1433 Some(&b"%ZZ"[..])
1434 );
1435 }
1436
1437 #[test]
1438 fn test_percent_decode_bytes_into_buffer_overflow_fail_closed() {
1439 let mut tiny = [0u8; 2];
1441 assert_eq!(percent_decode_bytes_into(b"abcdef", &mut tiny, true), None);
1442 let mut exact = [0u8; 6];
1444 assert_eq!(
1445 percent_decode_bytes_into(b"abcdef", &mut exact, true),
1446 Some(&b"abcdef"[..])
1447 );
1448 let mut empty: [u8; 0] = [];
1450 assert_eq!(
1451 percent_decode_bytes_into(b"", &mut empty, true),
1452 Some(&b""[..])
1453 );
1454 }
1455
1456 #[test]
1457 fn test_hex_value_all() {
1458 assert_eq!(hex_value(b'0'), Some(0));
1459 assert_eq!(hex_value(b'9'), Some(9));
1460 assert_eq!(hex_value(b'a'), Some(10));
1461 assert_eq!(hex_value(b'f'), Some(15));
1462 assert_eq!(hex_value(b'A'), Some(10));
1463 assert_eq!(hex_value(b'F'), Some(15));
1464 assert_eq!(hex_value(b'g'), None);
1465 assert_eq!(hex_value(b'G'), None);
1466 assert_eq!(hex_value(b' '), None);
1467 assert_eq!(hex_value(b'%'), None);
1468 }
1469
1470 #[test]
1475 fn test_normalize_query_multiple_question_marks() {
1476 let result = normalize_query("??key=value??foo=bar", true);
1478 assert_eq!(result, "key=value??foo=bar");
1479 }
1480
1481 #[test]
1482 fn test_normalize_query_whitespace_normalization() {
1483 let result = normalize_query("key=hello%20world\t\n", true);
1485 assert_eq!(result, "key=hello%20world ");
1486 }
1487
1488 #[test]
1489 fn test_normalize_query_decode_true_unreserved_only() {
1490 assert_eq!(normalize_query("a=%41%2e%2e&b=%7E", true), "a=A..&b=~");
1492 assert_eq!(normalize_query("p=%2f%3F%23", true), "p=%2f%3F%23");
1494 assert_eq!(normalize_query("q=100%+pure", true), "q=100%+pure");
1496 }
1497
1498 #[test]
1499 fn test_normalize_query_decode_false_passthrough() {
1500 assert_eq!(normalize_query("a=%41%2e&b=%2f", false), "a=%41%2e&b=%2f");
1502 assert_eq!(normalize_query("a=b\tc", false), "a=b c");
1504 }
1505
1506 #[test]
1507 fn test_normalize_query_only_question_marks() {
1508 let result = normalize_query("???", true);
1509 assert_eq!(result, "");
1510 }
1511
1512 #[test]
1513 fn test_normalize_query_complex() {
1514 let result = normalize_query("?a=1&b=2&c=3", true);
1515 assert_eq!(result, "a=1&b=2&c=3");
1516 }
1517
1518 #[test]
1523 fn test_normalize_authority_empty() {
1524 let result = normalize_authority("", "http");
1525 assert_eq!(result, "");
1526 }
1527
1528 #[test]
1529 fn test_normalize_authority_no_port_http() {
1530 let result = normalize_authority("example.com", "http");
1531 assert_eq!(result, "example.com");
1532 }
1533
1534 #[test]
1535 fn test_normalize_authority_no_port_https() {
1536 let result = normalize_authority("example.com", "https");
1537 assert_eq!(result, "example.com");
1538 }
1539
1540 #[test]
1541 fn test_normalize_authority_host_lowercase() {
1542 let result = normalize_authority("EXAMPLE.COM", "http");
1543 assert_eq!(result, "example.com");
1544 }
1545
1546 #[test]
1547 fn test_normalize_authority_mixed_case() {
1548 let result = normalize_authority("My-Host.Example.COM:8080", "http");
1549 assert_eq!(result, "my-host.example.com:8080");
1550 }
1551
1552 #[test]
1553 fn test_normalize_authority_non_numeric_port() {
1554 let result = normalize_authority("example.com:http", "http");
1556 assert_eq!(result, "example.com:http");
1557 }
1558
1559 #[test]
1560 fn test_normalize_authority_ipv6_no_port() {
1561 let result = normalize_authority("[::1]", "http");
1562 assert_eq!(result, "[::1]");
1563 }
1564
1565 #[test]
1566 fn test_normalize_authority_ipv6_default_port() {
1567 let result = normalize_authority("[::1]:443", "https");
1568 assert_eq!(result, "[::1]");
1569 }
1570
1571 #[test]
1572 fn test_normalize_authority_ipv6_unclosed_bracket() {
1573 let result = normalize_authority("[::1:8080", "http");
1574 assert_eq!(result, "[::1:8080");
1575 }
1576
1577 #[test]
1578 fn test_normalize_authority_unknown_scheme() {
1579 let result = normalize_authority("example.com:1234", "ftp");
1581 assert_eq!(result, "example.com:1234");
1582 }
1583
1584 #[test]
1589 fn test_normalize_headers_pseudo_headers_filtered() {
1590 let headers = [
1591 (":method", "GET"),
1592 (":path", "/test"),
1593 (":scheme", "https"),
1594 ("content-type", "application/json"),
1595 ];
1596 let result = normalize_request(
1597 Method::Get,
1598 "https",
1599 "example.com",
1600 "/test",
1601 "",
1602 &headers,
1603 Protocol::Http2,
1604 Transport::Tls13,
1605 )
1606 .unwrap();
1607
1608 assert_eq!(result.header_count(), 1);
1610 assert!(result.find_header("content-type").is_some());
1611 }
1612
1613 #[test]
1614 fn test_normalize_headers_value_trim() {
1615 let headers = [("X-Test", " hello world ")];
1616 let result = normalize_request(
1617 Method::Get,
1618 "http",
1619 "example.com",
1620 "/",
1621 "",
1622 &headers,
1623 Protocol::Http1,
1624 Transport::Plaintext,
1625 )
1626 .unwrap();
1627
1628 let hdr = result.find_header("x-test").unwrap();
1629 assert_eq!(hdr.value_str(), "hello world");
1630 }
1631
1632 #[test]
1633 fn test_normalize_headers_empty_value() {
1634 let headers = [("X-Empty", ""), ("X-Normal", "value")];
1635 let result = normalize_request(
1636 Method::Get,
1637 "http",
1638 "example.com",
1639 "/",
1640 "",
1641 &headers,
1642 Protocol::Http1,
1643 Transport::Plaintext,
1644 )
1645 .unwrap();
1646
1647 assert_eq!(result.header_count(), 2);
1648 assert_eq!(result.find_header("x-empty").unwrap().value_str(), "");
1649 }
1650
1651 #[test]
1652 fn test_normalize_headers_no_merge_mode() {
1653 let config = NormalizeConfig {
1654 merge_headers: false,
1655 ..NormalizeConfig::default()
1656 };
1657 let headers = [("X-Custom", "v1"), ("x-custom", "v2")];
1658 let result = normalize_request_with_config(
1659 Method::Get,
1660 "http",
1661 "example.com",
1662 "/",
1663 "",
1664 &headers,
1665 Protocol::Http1,
1666 Transport::Plaintext,
1667 config,
1668 )
1669 .unwrap();
1670
1671 assert_eq!(result.header_count(), 2);
1673 }
1674
1675 #[test]
1676 fn test_normalize_headers_no_normalize_case() {
1677 let config = NormalizeConfig {
1678 normalize_case: false,
1679 ..NormalizeConfig::default()
1680 };
1681 let headers = [("Content-Type", "application/json")];
1682 let result = normalize_request_with_config(
1683 Method::Get,
1684 "http",
1685 "example.com",
1686 "/",
1687 "",
1688 &headers,
1689 Protocol::Http1,
1690 Transport::Plaintext,
1691 config,
1692 )
1693 .unwrap();
1694
1695 assert!(result.find_header("Content-Type").is_some());
1698 assert!(result.find_header("content-type").is_some());
1699 let hdr = result.find_header("content-type").unwrap();
1700 assert_eq!(hdr.name_str(), "Content-Type");
1701 }
1702
1703 #[test]
1704 fn test_normalize_headers_all_config_off() {
1705 let config = NormalizeConfig {
1706 decode_path: false,
1707 merge_headers: false,
1708 normalize_case: false,
1709 };
1710 let headers = [("X-Test", "A"), ("x-test", "B")];
1711 let result = normalize_request_with_config(
1712 Method::Get,
1713 "HTTP",
1714 "Example.COM",
1715 "/hello%20world",
1716 "",
1717 &headers,
1718 Protocol::Http1,
1719 Transport::Plaintext,
1720 config,
1721 )
1722 .unwrap();
1723
1724 assert_eq!(result.scheme_str(), "HTTP");
1726 assert_eq!(result.path_str(), "/hello%20world");
1728 assert_eq!(result.header_count(), 2);
1730 }
1731
1732 #[test]
1733 fn test_normalize_headers_header_count_exceeded() {
1734 let mut header_names: Vec<String> = Vec::new();
1735 for i in 0..100 {
1736 header_names.push(format!("X-Header-{}", i));
1737 }
1738 let header_refs: Vec<(&str, &str)> = header_names.iter().map(|k| (k.as_str(), "value")).collect();
1739
1740 let result = normalize_request(
1741 Method::Get,
1742 "http",
1743 "example.com",
1744 "/",
1745 "",
1746 &header_refs,
1747 Protocol::Http1,
1748 Transport::Plaintext,
1749 );
1750
1751 assert!(result.is_err());
1753 }
1754
1755 #[test]
1760 fn test_is_valid_header_name_all_special_chars() {
1761 assert!(is_valid_header_name("!#$%&'*+-.^_`|~"));
1763 }
1764
1765 #[test]
1766 fn test_is_valid_header_name_empty() {
1767 assert!(!is_valid_header_name(""));
1768 }
1769
1770 #[test]
1771 fn test_is_valid_header_name_with_spaces() {
1772 assert!(!is_valid_header_name("content type"));
1773 }
1774
1775 #[test]
1776 fn test_is_valid_header_name_with_colon() {
1777 assert!(!is_valid_header_name("content-type:"));
1778 }
1779
1780 #[test]
1781 fn test_is_valid_header_name_with_newline() {
1782 assert!(!is_valid_header_name("content\r\ntype"));
1783 }
1784
1785 #[test]
1786 fn test_is_valid_header_name_with_null() {
1787 assert!(!is_valid_header_name("content\0type"));
1788 }
1789
1790 #[test]
1795 fn test_normalize_error_display() {
1796 let err = NormalizeError {
1797 message: "test error message",
1798 };
1799 assert_eq!(
1800 format!("{}", err),
1801 "NormalizeError: test error message"
1802 );
1803 }
1804
1805 #[test]
1810 fn test_normalize_config_default() {
1811 let config = NormalizeConfig::default();
1812 assert!(config.decode_path);
1813 assert!(config.merge_headers);
1814 assert!(config.normalize_case);
1815 }
1816
1817 #[test]
1822 fn test_full_normalize_http1_plaintext() {
1823 let result = normalize_request(
1824 Method::Get,
1825 "http",
1826 "example.com:80",
1827 "/path/../to/./resource",
1828 "?q=test",
1829 &[("Host", "example.com"), ("Accept", "text/html")],
1830 Protocol::Http1,
1831 Transport::Plaintext,
1832 )
1833 .unwrap();
1834
1835 assert_eq!(result.method, Method::Get);
1836 assert_eq!(result.protocol, Protocol::Http1);
1837 assert_eq!(result.transport, Transport::Plaintext);
1838 assert_eq!(result.scheme_str(), "http");
1839 assert_eq!(result.authority_str(), "example.com");
1840 assert_eq!(result.path_str(), "/to/resource");
1841 assert_eq!(result.query_str(), "q=test");
1842 }
1843
1844 #[test]
1845 fn test_full_normalize_http2_tls() {
1846 let result = normalize_request(
1847 Method::Post,
1848 "https",
1849 "api.example.com:443",
1850 "/api/v1/data",
1851 "verbose=true",
1852 &[
1853 (":method", "POST"),
1854 (":scheme", "https"),
1855 (":path", "/api/v1/data"),
1856 ("content-type", "application/json"),
1857 ("content-type", "text/plain"),
1858 ],
1859 Protocol::Http2,
1860 Transport::Tls13,
1861 )
1862 .unwrap();
1863
1864 assert_eq!(result.protocol, Protocol::Http2);
1865 assert_eq!(result.transport, Transport::Tls13);
1866 assert_eq!(result.header_count(), 1);
1868 let ct = result.find_header("content-type").unwrap();
1870 assert!(ct.value_str().contains("application/json"));
1871 assert!(ct.value_str().contains("text/plain"));
1872 }
1873
1874 #[test]
1875 fn test_full_normalize_http3() {
1876 let result = normalize_request(
1877 Method::Get,
1878 "https",
1879 "quic.example.com",
1880 "/",
1881 "",
1882 &[("user-agent", "test-agent")],
1883 Protocol::Http3,
1884 Transport::Tls13,
1885 )
1886 .unwrap();
1887
1888 assert_eq!(result.protocol, Protocol::Http3);
1889 assert_eq!(result.transport, Transport::Tls13);
1890 assert_eq!(result.path_str(), "/");
1891 }
1892
1893 #[test]
1898 fn test_normalize_all_methods() {
1899 let methods = [
1900 Method::Get,
1901 Method::Post,
1902 Method::Put,
1903 Method::Delete,
1904 Method::Patch,
1905 Method::Head,
1906 Method::Options,
1907 Method::Connect,
1908 Method::Trace,
1909 ];
1910
1911 for method in methods.iter() {
1912 let result = normalize_request(
1913 *method,
1914 "http",
1915 "example.com",
1916 "/",
1917 "",
1918 &[],
1919 Protocol::Http1,
1920 Transport::Plaintext,
1921 )
1922 .unwrap();
1923 assert_eq!(result.method, *method);
1924 }
1925 }
1926
1927 #[test]
1932 fn test_normalize_request_overlong_path_rejected() {
1933 let long_path = format!("/{}", "a".repeat(crate::MAX_PATH_LEN + 1));
1934 let result = normalize_request(
1935 Method::Get,
1936 "http",
1937 "example.com",
1938 &long_path,
1939 "",
1940 &[],
1941 Protocol::Http1,
1942 Transport::Plaintext,
1943 );
1944 let err = result.expect_err("超长路径必须报错");
1945 assert_eq!(err.message, "path too long");
1946 }
1947
1948 #[test]
1949 fn test_normalize_request_overlong_query_rejected() {
1950 let long_query = "a".repeat(crate::MAX_QUERY_LEN + 1);
1951 let result = normalize_request(
1952 Method::Get,
1953 "http",
1954 "example.com",
1955 "/",
1956 &long_query,
1957 &[],
1958 Protocol::Http1,
1959 Transport::Plaintext,
1960 );
1961 let err = result.expect_err("超长 query 必须报错");
1962 assert_eq!(err.message, "query too long");
1963 }
1964
1965 #[test]
1966 fn test_normalize_request_overlong_authority_rejected() {
1967 let long_authority = "a".repeat(crate::MAX_AUTHORITY_LEN + 1);
1968 let result = normalize_request(
1969 Method::Get,
1970 "http",
1971 &long_authority,
1972 "/",
1973 "",
1974 &[],
1975 Protocol::Http1,
1976 Transport::Plaintext,
1977 );
1978 let err = result.expect_err("超长 authority 必须报错");
1979 assert_eq!(err.message, "authority too long");
1980 }
1981
1982 #[test]
1983 fn test_normalize_request_overlong_scheme_rejected() {
1984 let result = normalize_request(
1986 Method::Get,
1987 "superlongscheme",
1988 "example.com",
1989 "/",
1990 "",
1991 &[],
1992 Protocol::Http1,
1993 Transport::Plaintext,
1994 );
1995 let err = result.expect_err("超长 scheme 必须报错");
1996 assert_eq!(err.message, "scheme too long");
1997 }
1998
1999 #[test]
2004 fn test_authority_vs_host_key_semantics_lock() {
2005 assert_eq!(
2008 normalize_authority("Example.COM:443", "http"),
2009 "example.com:443"
2010 );
2011 assert_eq!(normalize_host_key("Example.COM:443"), "example.com");
2012 assert_eq!(
2014 normalize_authority("Example.COM:443", "https"),
2015 "example.com"
2016 );
2017
2018 assert_eq!(normalize_authority("example.com.", "http"), "example.com.");
2020 assert_eq!(normalize_host_key("example.com."), "example.com");
2021
2022 assert_eq!(normalize_authority("[::1]:443", "http"), "[::1]:443");
2024 assert_eq!(normalize_host_key("[::1]:443"), "[::1]");
2025 assert_eq!(normalize_authority("[::1]:443", "https"), "[::1]");
2027
2028 assert_eq!(normalize_host_key(" example.com "), "example.com");
2030 assert_eq!(
2031 normalize_authority(" example.com ", "http"),
2032 " example.com "
2033 );
2034 }
2035}