1use alloc::vec::Vec;
14use core::fmt;
15
16use crate::{
17 error::HttpError,
18 head::{
19 Asked, Field, FieldSpan, HeadKind, LineScan, RequestHead, RequestInfo, ResponseHead,
20 ResponseInfo, Scan, SliceSink, Span, check_target, interpret_request, interpret_response,
21 parse_version,
22 },
23 limits::Limits,
24 method::Method,
25 scan::{check_field_value, find_byte, is_ows, is_token},
26};
27
28#[derive(Clone, Copy)]
58pub struct RawHead<'a> {
59 bytes: &'a [u8],
60 start_line: Span,
61 method: Span,
62 target: Span,
63 version: Span,
64 status: Span,
65 fields: &'a [FieldSpan],
66}
67
68impl<'a> RawHead<'a> {
69 #[must_use]
75 pub fn bytes(&self) -> &'a [u8] {
76 self.bytes
77 }
78
79 #[must_use]
92 pub fn start_line(&self) -> Span {
93 self.start_line
94 }
95
96 #[must_use]
102 pub fn method(&self) -> Span {
103 self.method
104 }
105
106 #[must_use]
112 pub fn target(&self) -> Span {
113 self.target
114 }
115
116 #[must_use]
122 pub fn version(&self) -> Span {
123 self.version
124 }
125
126 #[must_use]
141 pub fn status(&self) -> Span {
142 self.status
143 }
144
145 #[must_use]
151 pub fn fields(&self) -> &'a [FieldSpan] {
152 self.fields
153 }
154}
155
156impl fmt::Debug for RawHead<'_> {
157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158 f.debug_struct("RawHead")
159 .field("len", &self.bytes.len())
160 .field("start_line", &self.start_line)
161 .field("fields", &self.fields.len())
162 .finish_non_exhaustive()
163 }
164}
165
166#[derive(Clone, Copy, Debug, PartialEq, Eq)]
187pub struct HeadFacts {
188 info: RequestInfo,
189}
190
191fn head_len(bytes: &[u8], start_line: Span, fields: &[Field]) -> usize {
195 let after = fields.last().map_or(start_line.end, |f| f.value.end) as usize;
196 match bytes.get(after..).and_then(|rest| find_byte(b'\n', rest)) {
197 Some(lf) => (after + lf + 1 + 2).min(bytes.len()),
198 None => bytes.len(),
199 }
200}
201
202impl<'a> RequestHead<'a> {
203 #[must_use]
210 pub fn raw(&self) -> RawHead<'a> {
211 let (head, fields, info) = self.parts();
212 let sl = info.start_line;
213 let bytes = head.get(..head_len(head, sl, fields)).unwrap_or(head);
214 RawHead {
215 bytes,
216 start_line: sl,
217 method: info.method_span,
218 target: info.target,
219 version: Span {
220 start: info.target.end.saturating_add(1),
221 end: sl.end,
222 },
223 status: Span::default(),
224 fields,
225 }
226 }
227
228 pub fn copy_into(
256 &self,
257 bytes: &mut Vec<u8>,
258 fields: &mut Vec<FieldSpan>,
259 ) -> Result<HeadFacts, HttpError> {
260 let raw = self.raw();
261 bytes.clear();
262 bytes.extend_from_slice(raw.bytes);
263 fields.clear();
264 fields.extend_from_slice(raw.fields);
265 Ok(HeadFacts {
266 info: *self.parts().2,
267 })
268 }
269
270 #[must_use]
286 #[allow(clippy::wrong_self_convention)]
287 pub fn to_owned(&self) -> OwnedRequestHead {
288 let raw = self.raw();
289 OwnedRequestHead {
290 bytes: raw.bytes.to_vec(),
291 fields: raw.fields.to_vec(),
292 facts: HeadFacts {
293 info: *self.parts().2,
294 },
295 }
296 }
297}
298
299impl<'a> ResponseHead<'a> {
300 #[must_use]
306 pub fn raw(&self) -> RawHead<'a> {
307 let (head, fields, info) = self.parts();
308 let sl = info.start_line;
309 let bytes = head.get(..head_len(head, sl, fields)).unwrap_or(head);
310 RawHead {
311 bytes,
312 start_line: sl,
313 method: Span::default(),
314 target: Span::default(),
315 version: Span {
316 start: sl.start,
317 end: sl.start.saturating_add(8).min(sl.end),
318 },
319 status: Span {
320 start: sl.start.saturating_add(9).min(sl.end),
321 end: sl.start.saturating_add(12).min(sl.end),
322 },
323 fields,
324 }
325 }
326}
327
328#[derive(Clone, PartialEq, Eq)]
337pub struct OwnedRequestHead {
338 bytes: Vec<u8>,
339 fields: Vec<FieldSpan>,
340 facts: HeadFacts,
341}
342
343impl OwnedRequestHead {
344 #[must_use]
350 pub fn view(&self) -> RequestHead<'_> {
351 RequestHead::new(&self.bytes, &self.fields, &self.facts.info)
352 }
353
354 pub fn from_parts(
390 bytes: Vec<u8>,
391 fields: Vec<FieldSpan>,
392 facts: HeadFacts,
393 ) -> Result<Self, HttpError> {
394 validate(&bytes, &fields, &facts.info).map_err(HttpError::InvalidState)?;
395 Ok(Self {
396 bytes,
397 fields,
398 facts,
399 })
400 }
401
402 #[must_use]
420 pub fn into_parts(self) -> (Vec<u8>, Vec<FieldSpan>, HeadFacts) {
421 (self.bytes, self.fields, self.facts)
422 }
423}
424
425impl fmt::Debug for OwnedRequestHead {
426 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
427 fmt::Debug::fmt(&self.view(), f)
428 }
429}
430
431fn validate(bytes: &[u8], fields: &[Field], info: &RequestInfo) -> Result<(), &'static str> {
433 let len = bytes.len();
434 let sl = info.start_line;
435 if !sl.fits(len) || sl.is_empty() {
436 return Err("the start line span is outside the head");
437 }
438 let before = Span {
440 start: 0,
441 end: sl.start,
442 }
443 .get(bytes);
444 if before.len() % 2 != 0 || !before.chunks(2).all(|c| c == b"\r\n") {
445 return Err("bytes before the start line are not empty lines");
446 }
447 if bytes.get(sl.end as usize..sl.end as usize + 2) != Some(b"\r\n") {
448 return Err("the start line does not end in CRLF");
449 }
450 let m = info.method_span;
452 let t = info.target;
453 if m.start != sl.start || m.is_empty() || !m.fits(len) {
454 return Err("the method span does not start the start line");
455 }
456 if !is_token(m.get(bytes)) || Method::from_bytes(m.get(bytes)) != info.method {
457 return Err("the method bytes do not match the facts");
458 }
459 if t.start != m.end + 1
460 || t.is_empty()
461 || t.end >= sl.end
462 || bytes.get(m.end as usize) != Some(&b' ')
463 {
464 return Err("the target span is not after the method");
465 }
466 if bytes.get(t.end as usize) != Some(&b' ') {
467 return Err("the target is not followed by a space");
468 }
469 if check_target(info.method, t.get(bytes)).is_err() {
470 return Err("the target bytes are not a valid target for the method");
471 }
472 let version = Span {
473 start: t.end + 1,
474 end: sl.end,
475 };
476 if parse_version(version.get(bytes)) != Ok(info.version) {
477 return Err("the version bytes do not match the facts");
478 }
479 let inside_target = |s: Span| s.start >= t.start && s.end <= t.end && s.start <= s.end;
480 if !inside_target(info.origin) {
481 return Err("the origin span is outside the target");
482 }
483 if info.authority.is_some_and(|a| !inside_target(a)) {
484 return Err("the authority span is outside the target");
485 }
486 let mut pos = sl.end as usize + 2;
488 for field in fields {
489 let (name, value) = (field.name, field.value);
490 if name.start as usize != pos || name.is_empty() || !name.fits(len) {
491 return Err("a field line does not start where the previous line ended");
492 }
493 if !is_token(name.get(bytes)) || bytes.get(name.end as usize) != Some(&b':') {
494 return Err("a field name is not a token followed by a colon");
495 }
496 if !value.fits(len) || value.start < name.end + 1 {
497 return Err("a field value span is outside its line");
498 }
499 let lead = Span {
500 start: name.end + 1,
501 end: value.start,
502 };
503 if !lead.get(bytes).iter().all(|&b| is_ows(b)) {
504 return Err("bytes between a colon and its value are not whitespace");
505 }
506 let v = value.get(bytes);
507 if v.first().is_some_and(|&b| is_ows(b)) || v.last().is_some_and(|&b| is_ows(b)) {
508 return Err("a field value has surrounding whitespace");
509 }
510 if check_field_value(v).is_err() {
511 return Err("a field value holds a byte a field may not");
512 }
513 let mut end = value.end as usize;
514 while bytes.get(end).is_some_and(|&b| is_ows(b)) {
515 end += 1;
516 }
517 if bytes.get(end..end + 2) != Some(b"\r\n") {
518 return Err("a field line does not end in CRLF after its value");
519 }
520 pos = end + 2;
521 }
522 if pos + 2 != len || bytes.get(pos..) != Some(b"\r\n") {
523 return Err("the head does not end with the blank line");
524 }
525 if let Some(host) = info.host {
526 let is_host_value = fields
527 .iter()
528 .any(|f| f.value == host && f.name.get(bytes).eq_ignore_ascii_case(b"host"));
529 if !is_host_value {
530 return Err("the host span is not the value of a Host field");
531 }
532 }
533 Ok(())
534}
535
536#[non_exhaustive]
550#[derive(Debug)]
551pub enum Parsed<T> {
552 Complete {
555 head: T,
557 len: usize,
559 },
560 Partial,
562}
563
564#[derive(Clone, Copy)]
571pub struct ParsedRequest<'a> {
572 bytes: &'a [u8],
573 fields: &'a [FieldSpan],
574 info: RequestInfo,
575}
576
577impl ParsedRequest<'_> {
578 #[must_use]
584 pub fn view(&self) -> RequestHead<'_> {
585 RequestHead::new(self.bytes, self.fields, &self.info)
586 }
587
588 #[must_use]
601 pub fn facts(&self) -> HeadFacts {
602 HeadFacts { info: self.info }
603 }
604}
605
606impl fmt::Debug for ParsedRequest<'_> {
607 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
608 fmt::Debug::fmt(&self.view(), f)
609 }
610}
611
612#[derive(Clone, Copy)]
618pub struct ParsedResponse<'a> {
619 bytes: &'a [u8],
620 fields: &'a [FieldSpan],
621 info: ResponseInfo,
622}
623
624impl ParsedResponse<'_> {
625 #[must_use]
631 pub fn view(&self) -> ResponseHead<'_> {
632 ResponseHead::new(self.bytes, self.fields, &self.info)
633 }
634}
635
636impl fmt::Debug for ParsedResponse<'_> {
637 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
638 fmt::Debug::fmt(&self.view(), f)
639 }
640}
641
642pub fn parse_request_head<'a>(
676 input: &'a [u8],
677 limits: &Limits,
678 fields: &'a mut [FieldSpan],
679) -> Result<Parsed<ParsedRequest<'a>>, HttpError> {
680 let mut lines = LineScan::new();
681 let mut sink = SliceSink::new(fields);
682 let len = match lines.advance(input, limits, HeadKind::Request, &mut sink)? {
683 Scan::Partial => return Ok(Parsed::Partial),
684 Scan::Done(len) => len,
685 };
686 let (slots, count) = sink.into_parts();
687 let slots: &'a [FieldSpan] = slots;
688 let fields = slots.get(..count).unwrap_or(&[]);
689 let bytes = input.get(..len).unwrap_or(input);
690 let info = interpret_request(bytes, lines.start_line(), fields, limits)?;
691 Ok(Parsed::Complete {
692 head: ParsedRequest {
693 bytes,
694 fields,
695 info,
696 },
697 len,
698 })
699}
700
701pub fn parse_response_head<'a>(
731 input: &'a [u8],
732 limits: &Limits,
733 asked: Asked,
734 fields: &'a mut [FieldSpan],
735) -> Result<Parsed<ParsedResponse<'a>>, HttpError> {
736 let mut lines = LineScan::new();
737 let mut sink = SliceSink::new(fields);
738 let len = match lines.advance(input, limits, HeadKind::Response, &mut sink)? {
739 Scan::Partial => return Ok(Parsed::Partial),
740 Scan::Done(len) => len,
741 };
742 let (slots, count) = sink.into_parts();
743 let slots: &'a [FieldSpan] = slots;
744 let fields = slots.get(..count).unwrap_or(&[]);
745 let bytes = input.get(..len).unwrap_or(input);
746 let info = interpret_response(bytes, lines.start_line(), fields, limits, asked)?;
747 Ok(Parsed::Complete {
748 head: ParsedResponse {
749 bytes,
750 fields,
751 info,
752 },
753 len,
754 })
755}
756
757#[cfg(test)]
758mod tests {
759 use alloc::vec;
760
761 use super::*;
762 use crate::{ServerConn, ServerEvent};
763
764 fn owned_parts(input: &[u8]) -> (Vec<u8>, Vec<FieldSpan>, HeadFacts) {
765 let mut conn = ServerConn::new();
766 let _ = conn.feed(input);
767 let Ok(ServerEvent::Request(req)) = conn.poll() else {
768 panic!("no request in {input:?}");
769 };
770 let (mut bytes, mut fields) = (Vec::new(), Vec::new());
771 let facts = req.copy_into(&mut bytes, &mut fields).unwrap();
772 (bytes, fields, facts)
773 }
774
775 #[test]
776 fn test_span_get_and_len_never_panic() {
777 let head = b"abc";
778 assert_eq!(Span { start: 0, end: 3 }.get(head), b"abc");
779 assert_eq!(Span { start: 2, end: 9 }.get(head), b"");
780 assert_eq!(Span { start: 3, end: 1 }.get(head), b"");
781 assert_eq!(Span { start: 3, end: 1 }.len(), 0);
782 assert_eq!(
783 Span {
784 start: u32::MAX,
785 end: u32::MAX
786 }
787 .get(head),
788 b""
789 );
790 assert!(Span { start: 1, end: 1 }.is_empty());
791 }
792
793 #[test]
794 fn test_raw_head_spans_point_at_the_parts() {
795 let (bytes, fields, _) =
796 owned_parts(b"\r\nPUT /f?q HTTP/1.0\r\nX: v \r\nHost: h\r\n\r\nbody");
797 assert_eq!(bytes, b"\r\nPUT /f?q HTTP/1.0\r\nX: v \r\nHost: h\r\n\r\n");
798 assert_eq!(fields.len(), 2);
799 assert_eq!(fields[0].value.get(&bytes), b"v");
800 let mut conn = ServerConn::new();
801 let _ = conn.feed(&bytes);
802 let Ok(ServerEvent::Request(req)) = conn.poll() else {
803 panic!()
804 };
805 let raw = req.raw();
806 assert_eq!(raw.bytes(), &bytes[..]);
807 assert_eq!(raw.start_line().get(raw.bytes()), b"PUT /f?q HTTP/1.0");
808 assert_eq!(raw.version().get(raw.bytes()), b"HTTP/1.0");
809 assert!(!alloc::format!("{raw:?}").contains("PUT"));
810 }
811
812 #[test]
813 fn test_owned_head_view_equals_original() {
814 let input = b"GET http://a.test/p?q=1 HTTP/1.1\r\nHost: b\r\nCookie: s=1\r\nVia: 1.1 x\r\nVia: 1.1 y\r\n\r\n";
815 let mut conn = ServerConn::new();
816 let _ = conn.feed(input);
817 let Ok(ServerEvent::Request(req)) = conn.poll() else {
818 panic!()
819 };
820 let owned = req.to_owned();
821 let view = owned.view();
822 assert_eq!(view.method_str(), req.method_str());
823 assert_eq!(view.target(), req.target());
824 assert_eq!(view.target_form(), req.target_form());
825 assert_eq!(view.path(), req.path());
826 assert_eq!(view.query(), req.query());
827 assert_eq!(view.host(), req.host());
828 assert_eq!(view.version(), req.version());
829 assert_eq!(view.framing(), req.framing());
830 assert_eq!(view.keep_alive(), req.keep_alive());
831 assert!(view.headers().eq(req.headers()));
832 assert!(view.header_all("via").eq(req.header_all("via")));
833 assert_eq!(alloc::format!("{owned:?}"), alloc::format!("{req:?}"));
834 let (b, f, facts) = owned_parts(input);
835 let rebuilt = OwnedRequestHead::from_parts(b, f, facts).unwrap();
836 assert_eq!(rebuilt, owned);
837 }
838
839 #[test]
840 fn test_from_parts_accepts_every_valid_copy() {
841 for input in [
842 &b"GET / HTTP/1.1\r\nHost: a\r\n\r\n"[..],
843 b"\r\n\r\nGET / HTTP/1.0\r\n\r\n",
844 b"OPTIONS * HTTP/1.1\r\nHost: a\r\nX:\r\nY: \t \r\n\r\n",
845 b"CONNECT a:443 HTTP/1.1\r\nHost: a:443\r\n\r\n",
846 b"POST /x HTTP/1.1\r\nHost: \r\nTransfer-Encoding: chunked\r\n\r\n",
847 ] {
848 let (b, f, facts) = owned_parts(input);
849 assert!(
850 OwnedRequestHead::from_parts(b, f, facts).is_ok(),
851 "{input:?}"
852 );
853 }
854 }
855
856 #[test]
858 fn test_from_parts_refuses_every_tampered_part() {
859 let input = b"GET /a HTTP/1.1\r\nHost: h\r\nX-Two: v\r\n\r\n";
860 let (bytes, fields, facts) = owned_parts(input);
861 let refused = |b: Vec<u8>, f: Vec<FieldSpan>, facts: HeadFacts| {
862 matches!(
863 OwnedRequestHead::from_parts(b, f, facts),
864 Err(HttpError::InvalidState(_))
865 )
866 };
867 let tamper_byte = |i: usize, b: u8| {
869 let mut v = bytes.clone();
870 v[i] = b;
871 v
872 };
873 assert!(refused(tamper_byte(0, b'P'), fields.clone(), facts)); assert!(refused(tamper_byte(3, b'_'), fields.clone(), facts)); assert!(refused(tamper_byte(5, b'#'), fields.clone(), facts)); assert!(refused(tamper_byte(13, b'2'), fields.clone(), facts)); assert!(refused(tamper_byte(15, b'X'), fields.clone(), facts)); assert!(refused(tamper_byte(17, b' '), fields.clone(), facts)); assert!(refused(tamper_byte(21, b'='), fields.clone(), facts)); assert!(refused(tamper_byte(23, 0), fields.clone(), facts)); assert!(refused(tamper_byte(24, b'x'), fields.clone(), facts)); let mut shorter = bytes.clone();
883 let _ = shorter.pop();
884 assert!(refused(shorter, fields.clone(), facts)); let mut longer = bytes.clone();
886 longer.push(b'x');
887 assert!(refused(longer, fields.clone(), facts)); let mut leading = b"x\r\n".to_vec();
889 leading.extend_from_slice(&bytes);
890 assert!(refused(leading, fields.clone(), facts)); let tamper_field = |i: usize, change: &dyn Fn(&mut FieldSpan)| {
893 let mut f = fields.clone();
894 change(&mut f[i]);
895 f
896 };
897 assert!(refused(
898 bytes.clone(),
899 tamper_field(0, &|f| f.name.end += 1),
900 facts
901 ));
902 assert!(refused(
903 bytes.clone(),
904 tamper_field(0, &|f| f.name.start += 1),
905 facts
906 ));
907 assert!(refused(
908 bytes.clone(),
909 tamper_field(0, &|f| f.value.end += 50),
910 facts
911 ));
912 assert!(refused(
913 bytes.clone(),
914 tamper_field(0, &|f| f.value.start = 0),
915 facts
916 ));
917 assert!(refused(
918 bytes.clone(),
919 tamper_field(1, &|f| f.value.end -= 1),
920 facts
921 ));
922 assert!(refused(
923 bytes.clone(),
924 tamper_field(1, &|f| *f = FieldSpan::default()),
925 facts
926 ));
927 let mut swapped = fields.clone();
928 swapped.swap(0, 1);
929 assert!(refused(bytes.clone(), swapped, facts)); let mut dropped = fields.clone();
931 let _ = dropped.pop();
932 assert!(refused(bytes.clone(), dropped, facts)); let mut doubled = fields.clone();
934 doubled.push(fields[1]);
935 assert!(refused(bytes.clone(), doubled, facts)); let tamper_facts = |change: &dyn Fn(&mut RequestInfo)| {
938 let mut info = facts.info;
939 change(&mut info);
940 HeadFacts { info }
941 };
942 assert!(refused(
943 bytes.clone(),
944 fields.clone(),
945 tamper_facts(&|i| i.start_line.end += 1)
946 ));
947 assert!(refused(
948 bytes.clone(),
949 fields.clone(),
950 tamper_facts(&|i| i.target.end += 1)
951 ));
952 assert!(refused(
953 bytes.clone(),
954 fields.clone(),
955 tamper_facts(&|i| i.method = Method::Post)
956 ));
957 assert!(refused(
958 bytes.clone(),
959 fields.clone(),
960 tamper_facts(&|i| i.host = Some(fields[1].value))
961 ));
962 assert!(refused(
963 bytes.clone(),
964 fields.clone(),
965 tamper_facts(&|i| i.origin = Span { start: 0, end: 3 })
966 ));
967 assert!(refused(
968 bytes.clone(),
969 fields.clone(),
970 tamper_facts(&|i| i.authority = Some(Span { start: 0, end: 3 }))
971 ));
972 assert!(OwnedRequestHead::from_parts(bytes, fields, facts).is_ok());
974 }
975
976 #[test]
977 fn test_stateless_request_parse_complete_partial_and_errors() {
978 let mut fields = [FieldSpan::default(); 4];
979 let input = b"GET / HTTP/1.1\r\nHost: a\r\n\r\nGET";
980 let Ok(Parsed::Complete { head, len }) =
981 parse_request_head(input, &Limits::new(), &mut fields)
982 else {
983 panic!()
984 };
985 assert_eq!(len, input.len() - 3);
986 assert_eq!(head.view().host(), Some("a"));
987 for cut in 0..len {
988 let mut fields = [FieldSpan::default(); 4];
989 assert!(matches!(
990 parse_request_head(&input[..cut], &Limits::new(), &mut fields),
991 Ok(Parsed::Partial)
992 ));
993 }
994 let mut fields = [FieldSpan::default(); 4];
995 assert_eq!(
996 parse_request_head(b"GET / HTTP/1.1\r\n\r\n", &Limits::new(), &mut fields).err(),
997 Some(HttpError::MissingHost)
998 );
999 assert_eq!(
1000 parse_request_head(
1001 b"GET / HTTP/1.1\nHost: a\r\n\r\n",
1002 &Limits::new(),
1003 &mut fields
1004 )
1005 .err(),
1006 Some(HttpError::BareLf)
1007 );
1008 let mut one = [FieldSpan::default(); 1];
1010 assert_eq!(
1011 parse_request_head(
1012 b"GET / HTTP/1.1\r\nHost: a\r\nX: 1\r\n\r\n",
1013 &Limits::new(),
1014 &mut one
1015 )
1016 .err(),
1017 Some(HttpError::TooManyHeaders)
1018 );
1019 let mut none: [FieldSpan; 0] = [];
1020 assert!(matches!(
1021 parse_request_head(b"GET / HTTP/1.0\r\n\r\n", &Limits::new(), &mut none),
1022 Ok(Parsed::Complete { .. })
1023 ));
1024 }
1025
1026 #[test]
1027 fn test_stateless_response_parse_follows_what_was_asked() {
1028 let mut fields = [FieldSpan::default(); 4];
1029 let input = b"HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nabc";
1030 let Ok(Parsed::Complete { head, len }) =
1031 parse_response_head(input, &Limits::new(), Asked::default(), &mut fields)
1032 else {
1033 panic!()
1034 };
1035 assert_eq!(head.view().framing(), crate::BodyFraming::Length(3));
1036 assert_eq!(&input[len..], b"abc");
1037 assert_eq!(
1038 head.view().raw().status().get(head.view().raw().bytes()),
1039 b"200"
1040 );
1041 let mut fields = [FieldSpan::default(); 4];
1042 let connect = Asked {
1043 head: false,
1044 connect: true,
1045 };
1046 let Ok(Parsed::Complete { head, .. }) =
1047 parse_response_head(input, &Limits::new(), connect, &mut fields)
1048 else {
1049 panic!()
1050 };
1051 assert_eq!(head.view().framing(), crate::BodyFraming::None);
1052 let mut fields = [FieldSpan::default(); 4];
1053 assert_eq!(
1054 parse_response_head(
1055 b"\r\nHTTP/1.1 200 OK\r\n\r\n",
1056 &Limits::new(),
1057 Asked::default(),
1058 &mut fields
1059 )
1060 .err(),
1061 Some(HttpError::InvalidStartLine)
1062 );
1063 let mut fields = [FieldSpan::default(); 4];
1064 assert!(matches!(
1065 parse_response_head(b"HTTP/1.1 103 Early Hints\r\nLink: </a>\r\n\r\n", &Limits::new(), Asked::default(), &mut fields),
1066 Ok(Parsed::Complete { head, .. }) if head.view().is_informational()
1067 ));
1068 let v = vec![b'x'; 3];
1069 let mut fields = [FieldSpan::default(); 4];
1070 assert!(matches!(
1071 parse_response_head(&v, &Limits::new(), Asked::default(), &mut fields),
1072 Ok(Parsed::Partial)
1073 ));
1074 }
1075}