http_net/head.rs
1//! Incremental scanning of message heads, their interpretation, and the
2//! borrowed views handed to callers.
3//!
4//! The scanner finds one CRLF-terminated line at a time and remembers where
5//! it stopped searching, so input that arrives one byte at a time is still
6//! examined once: head parsing is linear in the head's length no matter how
7//! it is split. Each line is fully validated when it completes; field
8//! positions are stored as offsets from the message start so the head can be
9//! read back without copying.
10
11use alloc::vec::Vec;
12use core::{fmt, slice};
13
14use crate::{
15 error::HttpError,
16 limits::Limits,
17 method::Method,
18 redact::{FieldName, FieldValue, Target},
19 scan::{
20 ListSplit, check_field_value, find_byte, is_host_value, is_ows, is_reason, is_target_chars,
21 is_tchar, is_token, list_elements, parse_decimal, printable_run, trim_ows,
22 },
23};
24
25/// A byte range of a message head: `start..end`, as offsets into
26/// [`RawHead::bytes`](crate::RawHead::bytes) (or into the input of a
27/// stateless parser).
28///
29/// Offsets are `u32` because the head and trailer budgets in [`Limits`]
30/// are each capped at 16 MiB, so
31/// [`Limits::buffer_capacity`](crate::Limits::buffer_capacity) stays far
32/// below `u32::MAX`; body bytes are counted in `u64` and never indexed.
33/// Spans stay valid when the head
34/// bytes are moved or copied, which is what makes a head cheap to copy into
35/// a passport ([`RequestHead::copy_into`]).
36///
37/// # Examples
38///
39/// ```
40/// use http_net::Span;
41///
42/// let head = b"GET / HTTP/1.1\r\n";
43/// let method = Span { start: 0, end: 3 };
44/// assert_eq!(method.get(head), b"GET");
45/// assert_eq!(method.len(), 3);
46/// // Out of range: an empty slice, never a panic.
47/// assert_eq!(Span { start: 10, end: 99 }.get(head), b"");
48/// ```
49#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
50pub struct Span {
51 /// The first byte.
52 pub start: u32,
53 /// One past the last byte.
54 pub end: u32,
55}
56
57impl Span {
58 #[inline]
59 pub(crate) fn new(start: usize, end: usize) -> Self {
60 // Offsets are bounded by the buffer capacity, which `Limits` keeps
61 // far below `u32::MAX`.
62 Self {
63 start: start as u32,
64 end: end as u32,
65 }
66 }
67
68 /// The bytes of `head` the span covers; empty when the span lies
69 /// outside `head` or is reversed.
70 ///
71 /// # Examples
72 ///
73 /// ```
74 /// let span = http_net::Span { start: 4, end: 5 };
75 /// assert_eq!(span.get(b"GET / HTTP/1.1"), b"/");
76 /// ```
77 #[inline]
78 #[must_use]
79 pub fn get<'b>(&self, head: &'b [u8]) -> &'b [u8] {
80 head.get(self.start as usize..self.end as usize)
81 .unwrap_or(&[])
82 }
83
84 /// The span's length in bytes (0 for a reversed span).
85 ///
86 /// # Examples
87 ///
88 /// ```
89 /// assert_eq!(http_net::Span { start: 2, end: 7 }.len(), 5);
90 /// ```
91 #[inline]
92 #[must_use]
93 pub const fn len(&self) -> u32 {
94 self.end.saturating_sub(self.start)
95 }
96
97 /// Whether the span covers no bytes.
98 ///
99 /// # Examples
100 ///
101 /// ```
102 /// assert!(http_net::Span::default().is_empty());
103 /// ```
104 #[inline]
105 #[must_use]
106 pub const fn is_empty(&self) -> bool {
107 self.len() == 0
108 }
109
110 /// Whether the span lies inside a head of `len` bytes, in order.
111 pub(crate) const fn fits(&self, len: usize) -> bool {
112 self.start <= self.end && (self.end as usize) <= len
113 }
114}
115
116/// One field line of a head: the name's span and the value's span (the
117/// value without surrounding whitespace), as offsets into the head bytes.
118///
119/// # Examples
120///
121/// ```
122/// use http_net::{ServerConn, ServerEvent};
123///
124/// let mut conn = ServerConn::new();
125/// let _ = conn.feed(b"GET / HTTP/1.1\r\nHost: a \r\n\r\n");
126/// let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
127/// let raw = req.raw();
128/// let host = raw.fields()[0];
129/// assert_eq!(host.name.get(raw.bytes()), b"Host");
130/// assert_eq!(host.value.get(raw.bytes()), b"a");
131/// ```
132#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
133pub struct FieldSpan {
134 /// The field name, as received.
135 pub name: Span,
136 /// The field value, without leading or trailing whitespace.
137 pub value: Span,
138}
139
140/// The connections' own name for a field record.
141pub(crate) type Field = FieldSpan;
142
143/// Removes the fields a trailer section may not carry and a recipient
144/// could act on if it merged them into the head: framing
145/// (`content-length`, `transfer-encoding`) and routing (`host`), RFC 9110
146/// section 6.5.1. They never frame the message here; dropping them keeps a
147/// proxy or an application from relaying or merging them.
148pub(crate) fn drop_forbidden_trailers(head: &[u8], fields: &mut Vec<Field>) {
149 fields.retain(|f| {
150 let name = f.name.get(head);
151 !(name.eq_ignore_ascii_case(b"content-length")
152 || name.eq_ignore_ascii_case(b"transfer-encoding")
153 || name.eq_ignore_ascii_case(b"host"))
154 });
155}
156
157/// Where scanned field records go: the connection's reusable vector, a
158/// caller's slice (the stateless parsers, which allocate nothing), or
159/// nowhere (trailers that are validated and discarded).
160pub(crate) trait FieldSink {
161 fn put(&mut self, field: Field) -> Result<(), HttpError>;
162}
163
164impl FieldSink for Vec<Field> {
165 #[inline]
166 fn put(&mut self, field: Field) -> Result<(), HttpError> {
167 self.push(field);
168 Ok(())
169 }
170}
171
172/// Validates fields without keeping them.
173pub(crate) struct Discard;
174
175impl FieldSink for Discard {
176 #[inline]
177 fn put(&mut self, _field: Field) -> Result<(), HttpError> {
178 Ok(())
179 }
180}
181
182/// Fills a caller's slice; a field that does not fit is `TooManyHeaders`.
183pub(crate) struct SliceSink<'s> {
184 slots: &'s mut [Field],
185 len: usize,
186}
187
188impl<'s> SliceSink<'s> {
189 pub(crate) fn new(slots: &'s mut [Field]) -> Self {
190 Self { slots, len: 0 }
191 }
192
193 /// The slice back, and how many records were written to its front.
194 pub(crate) fn into_parts(self) -> (&'s mut [Field], usize) {
195 (self.slots, self.len)
196 }
197}
198
199impl FieldSink for SliceSink<'_> {
200 #[inline]
201 fn put(&mut self, field: Field) -> Result<(), HttpError> {
202 let slot = self
203 .slots
204 .get_mut(self.len)
205 .ok_or(HttpError::TooManyHeaders)?;
206 *slot = field;
207 self.len += 1;
208 Ok(())
209 }
210}
211
212#[derive(Clone, Copy, Debug, PartialEq, Eq)]
213pub(crate) enum HeadKind {
214 Request,
215 Response,
216 Trailer,
217}
218
219#[derive(Debug, PartialEq, Eq)]
220pub(crate) enum Scan {
221 Partial,
222 /// The head is complete; the value is its length including the blank line.
223 Done(usize),
224}
225
226/// Finds the next CRLF-terminated line starting at `line_start`, resuming
227/// the search at `*scan`. Returns the end of the line content (the index of
228/// its CR). `budget` bounds the line length including CRLF.
229pub(crate) fn find_line(
230 msg: &[u8],
231 line_start: usize,
232 scan: &mut usize,
233 budget: usize,
234 too_long: HttpError,
235) -> Result<Option<usize>, HttpError> {
236 let from = (*scan).max(line_start);
237 let hay = msg.get(from..).unwrap_or(&[]);
238 match find_byte(b'\n', hay) {
239 Some(i) => {
240 let lf = from + i;
241 if lf + 1 - line_start > budget {
242 return Err(too_long);
243 }
244 if lf == line_start || msg.get(lf - 1) != Some(&b'\r') {
245 return Err(HttpError::BareLf);
246 }
247 *scan = lf + 1;
248 Ok(Some(lf - 1))
249 }
250 None => {
251 *scan = msg.len();
252 if msg.len().saturating_sub(line_start) >= budget {
253 Err(too_long)
254 } else {
255 Ok(None)
256 }
257 }
258 }
259}
260
261/// Resumable scanner over a head (start line plus field lines) or a trailer
262/// section (field lines only), keeping its fields in a reusable vector.
263pub(crate) struct HeadScanner {
264 lines: LineScan,
265 pub(crate) fields: Vec<Field>,
266}
267
268impl HeadScanner {
269 pub(crate) const fn new() -> Self {
270 Self {
271 lines: LineScan::new(),
272 fields: Vec::new(),
273 }
274 }
275
276 /// Prepares for the next message; keeps the field vector's allocation.
277 pub(crate) fn reset(&mut self) {
278 self.lines = LineScan::new();
279 self.fields.clear();
280 }
281
282 /// Fields scanned in the current section, stored or not.
283 pub(crate) fn field_count(&self) -> usize {
284 self.lines.count
285 }
286
287 pub(crate) fn start_line(&self) -> Span {
288 self.lines.start_line()
289 }
290
291 /// Scans `msg` (the bytes from the message start to the end of input).
292 /// With `store` the fields are kept in [`fields`](Self::fields);
293 /// otherwise they are validated and counted only.
294 #[inline]
295 pub(crate) fn advance(
296 &mut self,
297 msg: &[u8],
298 limits: &Limits,
299 kind: HeadKind,
300 store: bool,
301 ) -> Result<Scan, HttpError> {
302 if store {
303 self.lines.advance(msg, limits, kind, &mut self.fields)
304 } else {
305 self.lines.advance(msg, limits, kind, &mut Discard)
306 }
307 }
308}
309
310/// The line-by-line state of a head scan: where the search stopped and
311/// what has been counted. Shared by the connections and the stateless
312/// parsers; the fields go to a [`FieldSink`].
313pub(crate) struct LineScan {
314 scan: usize,
315 line_start: usize,
316 start_line: Option<Span>,
317 skipped: usize,
318 field_bytes: usize,
319 count: usize,
320}
321
322impl LineScan {
323 pub(crate) const fn new() -> Self {
324 Self {
325 scan: 0,
326 line_start: 0,
327 start_line: None,
328 skipped: 0,
329 field_bytes: 0,
330 count: 0,
331 }
332 }
333
334 pub(crate) fn start_line(&self) -> Span {
335 self.start_line.unwrap_or_default()
336 }
337
338 /// Scans `msg` (the bytes from the message start to the end of input),
339 /// handing each validated field to `sink`.
340 pub(crate) fn advance<S: FieldSink>(
341 &mut self,
342 msg: &[u8],
343 limits: &Limits,
344 kind: HeadKind,
345 sink: &mut S,
346 ) -> Result<Scan, HttpError> {
347 let (section_bytes, section_fields) = if kind == HeadKind::Trailer {
348 (limits.max_trailer_bytes(), limits.max_trailer_fields())
349 } else {
350 (limits.max_header_bytes(), limits.max_headers())
351 };
352 loop {
353 let in_start = kind != HeadKind::Trailer && self.start_line.is_none();
354 let (budget, too_long) = if in_start {
355 (
356 limits.max_start_line().saturating_sub(self.skipped),
357 HttpError::StartLineTooLong,
358 )
359 } else {
360 // One line may not exceed the field-line budget, nor what is
361 // left of the section; whichever is smaller decides the error.
362 let section = section_bytes.saturating_sub(self.field_bytes);
363 if limits.max_field_line() < section {
364 (limits.max_field_line(), HttpError::FieldLineTooLong)
365 } else {
366 (section, HttpError::HeadersTooLarge)
367 }
368 };
369 // The common field line in one pass. Anything else, and a line
370 // whose search already began on earlier input, takes the general
371 // path below, which decides exactly as it always has.
372 if !in_start && self.scan <= self.line_start {
373 if let Some((field, next)) = fast_field(msg, self.line_start) {
374 let line_len = next - self.line_start;
375 if line_len <= budget {
376 self.scan = next;
377 self.line_start = next;
378 self.field_bytes += line_len;
379 if self.field_bytes + 2 > section_bytes {
380 return Err(HttpError::HeadersTooLarge);
381 }
382 if self.count >= section_fields {
383 return Err(HttpError::TooManyHeaders);
384 }
385 self.count += 1;
386 sink.put(field)?;
387 continue;
388 }
389 }
390 }
391 let Some(end) = find_line(msg, self.line_start, &mut self.scan, budget, too_long)?
392 else {
393 return Ok(Scan::Partial);
394 };
395 let start = self.line_start;
396 let line = msg.get(start..end).unwrap_or(&[]);
397 let line_len = self.scan - start;
398 self.line_start = self.scan;
399
400 if in_start {
401 if line.is_empty() {
402 if kind == HeadKind::Request {
403 // RFC 9112 section 2.2: ignore empty lines before the
404 // request line. They count against its budget.
405 self.skipped += line_len;
406 continue;
407 }
408 return Err(HttpError::InvalidStartLine);
409 }
410 if find_byte(b'\r', line).is_some() {
411 return Err(HttpError::BareCr);
412 }
413 self.start_line = Some(Span::new(start, end));
414 continue;
415 }
416 if line.is_empty() {
417 return Ok(Scan::Done(self.scan));
418 }
419 self.field_bytes += line_len;
420 // Every field line must leave room for the blank line that ends
421 // the section, so the section as a whole fits the budget.
422 if self.field_bytes + 2 > section_bytes {
423 return Err(HttpError::HeadersTooLarge);
424 }
425 if self.count >= section_fields {
426 return Err(HttpError::TooManyHeaders);
427 }
428 let field = parse_field(line, start)?;
429 self.count += 1;
430 sink.put(field)?;
431 }
432 }
433}
434
435/// Validates one field line (`line` starts at offset `base` in the head).
436// Forced inline: the scanner is compiled once per field sink, and without
437// it this per-field call stays out of line (about 8% on the head
438// benchmark, 12% on client response parsing).
439#[inline(always)]
440fn parse_field(line: &[u8], base: usize) -> Result<Field, HttpError> {
441 if line.first().is_some_and(|&b| is_ows(b)) {
442 return Err(HttpError::ObsFold);
443 }
444 let Some(colon) = find_byte(b':', line) else {
445 return Err(if find_byte(b'\r', line).is_some() {
446 HttpError::BareCr
447 } else {
448 HttpError::InvalidHeaderName
449 });
450 };
451 let name = &line[..colon];
452 if name.last().is_some_and(|&b| is_ows(b)) {
453 return Err(HttpError::WhitespaceBeforeColon);
454 }
455 if !name.iter().all(|&b| is_tchar(b)) || name.is_empty() {
456 return Err(if find_byte(b'\r', name).is_some() {
457 HttpError::BareCr
458 } else {
459 HttpError::InvalidHeaderName
460 });
461 }
462 let raw = &line[colon + 1..];
463 let lead = raw.iter().take_while(|&&b| is_ows(b)).count();
464 let value = trim_ows(raw);
465 check_field_value(value)?;
466 let value_start = base + colon + 1 + lead;
467 Ok(Field {
468 name: Span::new(base, base + colon),
469 value: Span::new(value_start, value_start + value.len()),
470 })
471}
472
473/// The common field line in one pass, the way it nearly always arrives: a
474/// token name, a colon, spaces, a value of printable ASCII, and CR LF, all
475/// in `msg` from `start`. Returns the field and the offset after its LF.
476///
477/// `None` for anything else (a tab, obs-text, a control byte, a bare CR or
478/// LF, a fold, a missing colon) and for a line not yet complete: the caller
479/// then takes the general path from the line's start, which accepts and
480/// refuses exactly as before. On every line this accepts, the general path
481/// gives the same field (`tests` below and the fuzz targets check it).
482#[inline(always)]
483fn fast_field(msg: &[u8], start: usize) -> Option<(Field, usize)> {
484 let line = msg.get(start..)?;
485 let colon = line.iter().position(|&b| !is_tchar(b))?;
486 if colon == 0 || line[colon] != b':' {
487 return None;
488 }
489 let mut value = colon + 1;
490 while line.get(value) == Some(&b' ') {
491 value += 1;
492 }
493 let cr = value + printable_run(&line[value..]);
494 if line.get(cr) != Some(&b'\r') || line.get(cr + 1) != Some(&b'\n') {
495 return None;
496 }
497 let mut value_end = cr;
498 while value_end > value && line[value_end - 1] == b' ' {
499 value_end -= 1;
500 }
501 Some((
502 Field {
503 name: Span::new(start, start + colon),
504 value: Span::new(start + value, start + value_end),
505 },
506 start + cr + 2,
507 ))
508}
509
510/// The HTTP version of a message.
511///
512/// # Examples
513///
514/// ```
515/// use http_net::Version;
516///
517/// assert_eq!(Version::Http11.as_str(), "HTTP/1.1");
518/// ```
519#[non_exhaustive]
520#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
521pub enum Version {
522 /// HTTP/1.0: no chunked encoding, keep-alive only when asked for.
523 Http10,
524 /// HTTP/1.1 (also used for HTTP/1.2 through 1.9, which RFC 9110 section
525 /// 2.5 says to process as the highest implemented minor version).
526 Http11,
527}
528
529impl Version {
530 /// The version's wire text.
531 ///
532 /// # Examples
533 ///
534 /// ```
535 /// assert_eq!(http_net::Version::Http10.as_str(), "HTTP/1.0");
536 /// ```
537 #[must_use]
538 pub const fn as_str(&self) -> &'static str {
539 match self {
540 Self::Http10 => "HTTP/1.0",
541 Self::Http11 => "HTTP/1.1",
542 }
543 }
544}
545
546/// How a message's body is delimited.
547///
548/// # Examples
549///
550/// ```
551/// use http_net::{BodyFraming, ServerConn, ServerEvent};
552///
553/// let mut conn = ServerConn::new();
554/// let _ = conn.feed(b"POST /x HTTP/1.1\r\nHost: a\r\nContent-Length: 3\r\n\r\nabc");
555/// match conn.poll().unwrap() {
556/// ServerEvent::Request(req) => assert_eq!(req.framing(), BodyFraming::Length(3)),
557/// other => panic!("{other:?}"),
558/// }
559/// ```
560#[non_exhaustive]
561#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
562pub enum BodyFraming {
563 /// No body.
564 None,
565 /// Exactly this many bytes (`Content-Length`).
566 Length(u64),
567 /// `Transfer-Encoding: chunked`.
568 Chunked,
569 /// The body runs until the peer closes the connection (responses only).
570 UntilClose,
571}
572
573/// The form of a request target (RFC 9112 section 3.2).
574///
575/// # Examples
576///
577/// ```
578/// use http_net::{ServerConn, ServerEvent, TargetForm};
579///
580/// let mut conn = ServerConn::new();
581/// let _ = conn.feed(b"OPTIONS * HTTP/1.1\r\nHost: a\r\n\r\n");
582/// let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
583/// assert_eq!(req.target_form(), TargetForm::Asterisk);
584/// ```
585#[non_exhaustive]
586#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
587pub enum TargetForm {
588 /// `/path?query`: the usual form for requests to an origin server.
589 Origin,
590 /// `scheme://authority/path?query`: requests to a proxy (and, per RFC
591 /// 9112 section 3.2.2, acceptable to origin servers too).
592 Absolute,
593 /// `host:port`: only with `CONNECT`.
594 Authority,
595 /// `*`: only with `OPTIONS`, for the server as a whole.
596 Asterisk,
597}
598
599/// The `h2c` upgrade token, which is never treated as an upgrade offer
600/// (RFC 9113 section 3.1 deprecates it; forwarded blindly by a proxy, it is
601/// the h2c smuggling attack).
602pub(crate) fn is_h2c(protocol: &[u8]) -> bool {
603 let name = protocol
604 .iter()
605 .position(|&b| b == b'/')
606 .map_or(protocol, |i| &protocol[..i]);
607 name.eq_ignore_ascii_case(b"h2c")
608}
609
610/// Whether a `101` naming `chosen` answers an `Upgrade` offer `element`:
611/// ASCII case-insensitive, and a name without a version matches an offer of
612/// that name with any version (`websocket` answers `websocket/13`).
613pub(crate) fn protocol_matches(chosen: &[u8], element: &[u8]) -> bool {
614 if is_h2c(element) || is_h2c(chosen) {
615 return false;
616 }
617 if chosen.eq_ignore_ascii_case(element) {
618 return true;
619 }
620 !chosen.contains(&b'/')
621 && element
622 .iter()
623 .position(|&b| b == b'/')
624 .is_some_and(|i| element[..i].eq_ignore_ascii_case(chosen))
625}
626
627/// Whether a `101` switches only to protocols the request offered: the
628/// response's `Upgrade` fields name at least one protocol, and every one of
629/// them matches an element of `offer` (the request's `Upgrade` value).
630/// Names compare ASCII case-insensitively; a bare name matches a versioned
631/// one either way (`websocket` and `websocket/13`).
632pub(crate) fn switches_to_offered(head: &[u8], fields: &[Field], offer: &[u8]) -> bool {
633 let mut named = false;
634 for field in fields {
635 if !field.name.get(head).eq_ignore_ascii_case(b"upgrade") {
636 continue;
637 }
638 for chosen in list_elements(field.value.get(head)) {
639 named = true;
640 let offered = list_elements(offer)
641 .any(|o| protocol_matches(chosen, o) || protocol_matches(o, chosen));
642 if !offered {
643 return false;
644 }
645 }
646 }
647 named
648}
649
650pub(crate) fn parse_version(v: &[u8]) -> Result<Version, HttpError> {
651 match v {
652 [b'H', b'T', b'T', b'P', b'/', major, b'.', minor]
653 if major.is_ascii_digit() && minor.is_ascii_digit() =>
654 {
655 match (major, minor) {
656 (b'1', b'0') => Ok(Version::Http10),
657 (b'1', _) => Ok(Version::Http11),
658 _ => Err(HttpError::UnsupportedVersion),
659 }
660 }
661 _ => Err(HttpError::InvalidVersion),
662 }
663}
664
665/// What the framing-relevant fields of a message say.
666#[derive(Default)]
667struct FieldFacts {
668 content_length: Option<u64>,
669 te_seen: bool,
670 te_codings: usize,
671 te_chunked: usize,
672 te_last_chunked: bool,
673 te_other: bool,
674 close: bool,
675 keep_alive: bool,
676 upgrade_token: bool,
677 upgrade_field: bool,
678 /// An `Upgrade` element other than `h2c`.
679 upgrade_offer: bool,
680 expect_continue: bool,
681 /// An `Expect` element other than `100-continue`.
682 expect_other: bool,
683 hosts: usize,
684 host: Option<Span>,
685}
686
687fn scan_fields(head: &[u8], fields: &[Field]) -> Result<FieldFacts, HttpError> {
688 let mut facts = FieldFacts::default();
689 // The lengths of the six names below: most fields are ruled out before
690 // any compare.
691 const LENGTHS: u32 = (1 << 4) | (1 << 6) | (1 << 7) | (1 << 10) | (1 << 14) | (1 << 17);
692 for field in fields {
693 let name = field.name.get(head);
694 if name.len() > 17 || LENGTHS & (1 << name.len()) == 0 {
695 continue;
696 }
697 let value = field.value.get(head);
698 if name.eq_ignore_ascii_case(b"content-length") {
699 if facts.content_length.is_some() {
700 return Err(HttpError::DuplicateContentLength);
701 }
702 facts.content_length =
703 Some(parse_decimal(value).ok_or(HttpError::InvalidContentLength)?);
704 } else if name.eq_ignore_ascii_case(b"transfer-encoding") {
705 facts.te_seen = true;
706 for element in list_elements(value) {
707 let (coding, params) = match find_byte(b';', element) {
708 Some(i) => (trim_ows(&element[..i]), true),
709 None => (element, false),
710 };
711 if !is_token(coding) {
712 return Err(HttpError::InvalidTransferEncoding);
713 }
714 facts.te_codings += 1;
715 if coding.eq_ignore_ascii_case(b"chunked") {
716 if params {
717 return Err(HttpError::InvalidTransferEncoding);
718 }
719 facts.te_chunked += 1;
720 facts.te_last_chunked = true;
721 } else {
722 facts.te_other = true;
723 facts.te_last_chunked = false;
724 }
725 }
726 } else if name.eq_ignore_ascii_case(b"connection") {
727 for token in list_elements(value) {
728 if token.eq_ignore_ascii_case(b"close") {
729 facts.close = true;
730 } else if token.eq_ignore_ascii_case(b"keep-alive") {
731 facts.keep_alive = true;
732 } else if token.eq_ignore_ascii_case(b"upgrade") {
733 facts.upgrade_token = true;
734 }
735 }
736 } else if name.eq_ignore_ascii_case(b"upgrade") {
737 facts.upgrade_field |= !value.is_empty();
738 facts.upgrade_offer |= list_elements(value).any(|e| !is_h2c(e));
739 } else if name.eq_ignore_ascii_case(b"host") {
740 facts.hosts += 1;
741 facts.host = Some(field.value);
742 } else if name.eq_ignore_ascii_case(b"expect") {
743 // RFC 9110 section 10.1.1: the expectation is case-insensitive.
744 for e in list_elements(value) {
745 if e.eq_ignore_ascii_case(b"100-continue") {
746 facts.expect_continue = true;
747 } else {
748 facts.expect_other = true;
749 }
750 }
751 }
752 }
753 Ok(facts)
754}
755
756/// Applies RFC 9112 section 6 to the transfer-coding facts.
757fn transfer_framing(
758 facts: &FieldFacts,
759 version: Version,
760) -> Result<Option<BodyFraming>, HttpError> {
761 if !facts.te_seen {
762 return Ok(None);
763 }
764 if version == Version::Http10 {
765 return Err(HttpError::TransferEncodingInHttp10);
766 }
767 if facts.content_length.is_some() {
768 return Err(HttpError::ConflictingFraming);
769 }
770 if facts.te_codings == 0 || facts.te_chunked > 1 || !facts.te_last_chunked {
771 return Err(HttpError::InvalidTransferEncoding);
772 }
773 if facts.te_other {
774 return Err(HttpError::UnsupportedTransferEncoding);
775 }
776 Ok(Some(BodyFraming::Chunked))
777}
778
779/// Everything learned from a request head. Spans are offsets into the head
780/// bytes, so the facts stay valid when the bytes are copied.
781#[derive(Clone, Copy, Debug, PartialEq, Eq)]
782pub(crate) struct RequestInfo {
783 pub(crate) method: Method,
784 pub(crate) start_line: Span,
785 pub(crate) method_span: Span,
786 pub(crate) target: Span,
787 target_form: TargetForm,
788 pub(crate) version: Version,
789 pub(crate) framing: BodyFraming,
790 content_length: Option<u64>,
791 pub(crate) keep_alive: bool,
792 pub(crate) upgrade: bool,
793 pub(crate) expect_continue: bool,
794 unknown_expectation: bool,
795 pub(crate) host: Option<Span>,
796 /// The authority of an absolute-form target, which replaces `Host`.
797 pub(crate) authority: Option<Span>,
798 /// The path-and-query part of the target: the whole target except for
799 /// the absolute form, where it is what follows the authority.
800 pub(crate) origin: Span,
801}
802
803impl RequestInfo {
804 pub(crate) const EMPTY: Self = Self {
805 method: Method::Get,
806 start_line: Span { start: 0, end: 0 },
807 method_span: Span { start: 0, end: 0 },
808 target: Span { start: 0, end: 0 },
809 target_form: TargetForm::Origin,
810 version: Version::Http11,
811 framing: BodyFraming::None,
812 content_length: None,
813 keep_alive: false,
814 upgrade: false,
815 expect_continue: false,
816 unknown_expectation: false,
817 host: None,
818 authority: None,
819 origin: Span { start: 0, end: 0 },
820 };
821}
822
823/// Validates a request target against the forms its method allows.
824pub(crate) fn check_target(method: Method, target: &[u8]) -> Result<(), HttpError> {
825 let ok = match target.first() {
826 None => false,
827 Some(b'/') => method != Method::Connect && is_target_chars(target),
828 Some(b'*') if target.len() == 1 => method == Method::Options,
829 Some(_) if method == Method::Connect => {
830 // authority-form: host ":" port, with a non-empty host and port.
831 is_authority(target)
832 && target
833 .iter()
834 .rposition(|&b| b == b':')
835 .is_some_and(|c| c + 1 < target.len() && !target[c..].contains(&b']'))
836 }
837 Some(_) => split_absolute_form(target).is_some(),
838 };
839 if ok {
840 Ok(())
841 } else {
842 Err(HttpError::InvalidTarget)
843 }
844}
845
846/// `host [":" port]` with a non-empty host: RFC 9110 section 4.2.1 makes an
847/// empty host in an `http` URI invalid.
848fn is_authority(s: &[u8]) -> bool {
849 !s.is_empty() && s[0] != b':' && is_host_value(s)
850}
851
852/// Splits an absolute-form target, `scheme "://" authority path-abempty
853/// [ "?" query ]`, returning the authority's range within it.
854///
855/// Only the hierarchical form with an authority is accepted: it is the form
856/// RFC 9112 section 3.2.2 describes (requests to proxies, and future
857/// requests to origin servers), and it is the only one that names the host
858/// the request is for. Userinfo is refused (RFC 9110 section 4.2.4 says to
859/// treat it as an error), as is an empty host.
860fn split_absolute_form(target: &[u8]) -> Option<(usize, usize)> {
861 let colon = find_byte(b':', target)?;
862 let scheme = &target[..colon];
863 let scheme_ok = scheme.first().is_some_and(u8::is_ascii_alphabetic)
864 && scheme
865 .iter()
866 .all(|&b| b.is_ascii_alphanumeric() || matches!(b, b'+' | b'-' | b'.'));
867 if !scheme_ok || !target[colon..].starts_with(b"://") {
868 return None;
869 }
870 let auth_start = colon + 3;
871 let after = &target[auth_start..];
872 let auth_len = after
873 .iter()
874 .position(|&b| b == b'/' || b == b'?')
875 .unwrap_or(after.len());
876 let authority = &after[..auth_len];
877 let rest = &after[auth_len..];
878 (is_authority(authority) && is_target_chars(rest))
879 .then_some((auth_start, auth_start + auth_len))
880}
881
882pub(crate) fn interpret_request(
883 head: &[u8],
884 start: Span,
885 fields: &[Field],
886 limits: &Limits,
887) -> Result<RequestInfo, HttpError> {
888 let line = start.get(head);
889 let base = start.start as usize;
890 let sp1 = find_byte(b' ', line).ok_or(HttpError::InvalidStartLine)?;
891 let sp2 = line
892 .iter()
893 .rposition(|&b| b == b' ')
894 .ok_or(HttpError::InvalidStartLine)?;
895 if sp2 == sp1 {
896 return Err(HttpError::InvalidStartLine);
897 }
898 let method_bytes = &line[..sp1];
899 if !is_token(method_bytes) {
900 return Err(HttpError::InvalidMethod);
901 }
902 let version = parse_version(&line[sp2 + 1..])?;
903 let method = Method::from_bytes(method_bytes);
904 let target = &line[sp1 + 1..sp2];
905 check_target(method, target)?;
906 let target_start = base + sp1 + 1;
907 let target_span = Span::new(target_start, base + sp2);
908 let (target_form, authority, origin) = match target.first() {
909 Some(b'/') => (TargetForm::Origin, None, target_span),
910 Some(b'*') => (TargetForm::Asterisk, None, target_span),
911 _ if method == Method::Connect => (TargetForm::Authority, None, target_span),
912 _ => match split_absolute_form(target) {
913 Some((a, b)) => (
914 TargetForm::Absolute,
915 Some(Span::new(target_start + a, target_start + b)),
916 Span::new(target_start + b, base + sp2),
917 ),
918 None => return Err(HttpError::InvalidTarget),
919 },
920 };
921
922 let facts = scan_fields(head, fields)?;
923 let framing = match transfer_framing(&facts, version)? {
924 Some(f) => f,
925 None => match facts.content_length {
926 Some(n) if n > limits.max_body() => return Err(HttpError::BodyTooLarge),
927 Some(0) | None => BodyFraming::None,
928 Some(n) => BodyFraming::Length(n),
929 },
930 };
931 if facts.hosts > 1 {
932 return Err(HttpError::InvalidHost);
933 }
934 if let Some(host) = facts.host {
935 if !is_host_value(host.get(head)) {
936 return Err(HttpError::InvalidHost);
937 }
938 } else if version == Version::Http11 {
939 return Err(HttpError::MissingHost);
940 }
941 let keep_alive = match version {
942 Version::Http11 => !facts.close,
943 Version::Http10 => facts.keep_alive && !facts.close,
944 };
945 Ok(RequestInfo {
946 method,
947 start_line: start,
948 method_span: Span::new(base, base + sp1),
949 target: target_span,
950 target_form,
951 version,
952 framing,
953 content_length: facts.content_length,
954 keep_alive,
955 // RFC 9110 section 7.8: Upgrade in an HTTP/1.0 request is ignored.
956 // An offer of only `h2c` is no offer: the request is served as
957 // HTTP/1.1 (RFC 9113 section 3.1).
958 upgrade: version == Version::Http11 && facts.upgrade_token && facts.upgrade_offer,
959 // Reported for any version, so the caller can answer 417 (RFC 9110
960 // section 10.1.1 MAY); it never changes framing.
961 unknown_expectation: facts.expect_other,
962 // RFC 9110 section 10.1.1: ignored in HTTP/1.0, and meaningless
963 // without a body to wait for.
964 expect_continue: version == Version::Http11
965 && facts.expect_continue
966 && framing != BodyFraming::None,
967 host: facts.host,
968 authority,
969 origin,
970 })
971}
972
973/// Everything learned from a response head.
974#[derive(Clone, Copy, Debug, PartialEq, Eq)]
975pub(crate) struct ResponseInfo {
976 pub(crate) status: u16,
977 pub(crate) start_line: Span,
978 reason: Span,
979 pub(crate) version: Version,
980 pub(crate) framing: BodyFraming,
981 content_length: Option<u64>,
982 pub(crate) keep_alive: bool,
983 pub(crate) upgrade_field: bool,
984}
985
986impl ResponseInfo {
987 pub(crate) const EMPTY: Self = Self {
988 status: 0,
989 start_line: Span { start: 0, end: 0 },
990 reason: Span { start: 0, end: 0 },
991 version: Version::Http11,
992 framing: BodyFraming::None,
993 content_length: None,
994 keep_alive: false,
995 upgrade_field: false,
996 };
997}
998
999/// What the request asked for, which decides how its response is framed
1000/// (RFC 9112 section 6.3): a response to `HEAD` has no body whatever its
1001/// fields say, and a 2xx to `CONNECT` turns the connection into a tunnel.
1002#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
1003pub(crate) struct Asked {
1004 /// The request was `HEAD`.
1005 pub(crate) head: bool,
1006 /// The request was `CONNECT`.
1007 pub(crate) connect: bool,
1008}
1009
1010impl Asked {
1011 /// The facts of a request with `method`.
1012 pub(crate) fn from_method(method: Method) -> Self {
1013 Self {
1014 head: method == Method::Head,
1015 connect: method == Method::Connect,
1016 }
1017 }
1018}
1019
1020pub(crate) fn interpret_response(
1021 head: &[u8],
1022 start: Span,
1023 fields: &[Field],
1024 limits: &Limits,
1025 asked: Asked,
1026) -> Result<ResponseInfo, HttpError> {
1027 let line = start.get(head);
1028 let base = start.start as usize;
1029 // status-line = HTTP-version SP status-code SP [ reason-phrase ]
1030 // The SP after the code is required of senders, but some servers omit
1031 // it when the reason is empty (`HTTP/1.1 200`); accepting that cannot
1032 // change framing, so the client does (RFC 9112 section 4).
1033 if line.len() < 12 || line[8] != b' ' || (line.len() > 12 && line[12] != b' ') {
1034 return Err(HttpError::InvalidStartLine);
1035 }
1036 let version = parse_version(&line[..8])?;
1037 let code = &line[9..12];
1038 if !code.iter().all(u8::is_ascii_digit) {
1039 return Err(HttpError::InvalidStatus);
1040 }
1041 let status = code
1042 .iter()
1043 .fold(0u16, |acc, &d| acc * 10 + u16::from(d - b'0'));
1044 if !(100..=599).contains(&status) {
1045 return Err(HttpError::InvalidStatus);
1046 }
1047 let reason_start = line.len().min(13);
1048 let reason = &line[reason_start..];
1049 if !is_reason(reason) {
1050 return Err(HttpError::InvalidStartLine);
1051 }
1052
1053 let facts = scan_fields(head, fields)?;
1054 let bodiless = asked.head
1055 || (100..200).contains(&status)
1056 || status == 204
1057 || status == 304
1058 || (asked.connect && (200..300).contains(&status));
1059 let framing = if bodiless {
1060 BodyFraming::None
1061 } else {
1062 match transfer_framing(&facts, version)? {
1063 Some(f) => f,
1064 None => match facts.content_length {
1065 Some(n) if n > limits.max_body() => return Err(HttpError::BodyTooLarge),
1066 Some(0) => BodyFraming::None,
1067 Some(n) => BodyFraming::Length(n),
1068 None => BodyFraming::UntilClose,
1069 },
1070 }
1071 };
1072 let keep_alive = framing != BodyFraming::UntilClose
1073 && match version {
1074 Version::Http11 => !facts.close,
1075 Version::Http10 => facts.keep_alive && !facts.close,
1076 };
1077 Ok(ResponseInfo {
1078 status,
1079 start_line: start,
1080 reason: Span::new(base + reason_start, base + line.len()),
1081 version,
1082 framing,
1083 content_length: facts.content_length,
1084 keep_alive,
1085 upgrade_field: facts.upgrade_field,
1086 })
1087}
1088
1089/// Iterator over a head's fields as `(name, value)` pairs, in arrival order.
1090///
1091/// Names are returned as received (field names are case-insensitive; compare
1092/// with `eq_ignore_ascii_case`). Values have surrounding whitespace removed
1093/// and may contain any byte RFC 9110 allows, including `obs-text` above
1094/// 0x7F, so they are bytes rather than `str`.
1095///
1096/// # Examples
1097///
1098/// ```
1099/// use http_net::{ServerConn, ServerEvent};
1100///
1101/// let mut conn = ServerConn::new();
1102/// let _ = conn.feed(b"GET / HTTP/1.1\r\nHost: a\r\nX-One: 1\r\n\r\n");
1103/// if let ServerEvent::Request(req) = conn.poll().unwrap() {
1104/// let names: Vec<&str> = req.headers().map(|(n, _)| n).collect();
1105/// assert_eq!(names, ["Host", "X-One"]);
1106/// }
1107/// ```
1108#[derive(Clone)]
1109pub struct Headers<'a> {
1110 head: &'a [u8],
1111 fields: slice::Iter<'a, Field>,
1112}
1113
1114impl<'a> Headers<'a> {
1115 pub(crate) fn new(head: &'a [u8], fields: &'a [Field]) -> Self {
1116 Self {
1117 head,
1118 fields: fields.iter(),
1119 }
1120 }
1121}
1122
1123impl<'a> Iterator for Headers<'a> {
1124 type Item = (&'a str, &'a [u8]);
1125
1126 #[inline]
1127 fn next(&mut self) -> Option<Self::Item> {
1128 let field = self.fields.next()?;
1129 Some((
1130 core::str::from_utf8(field.name.get(self.head)).unwrap_or(""),
1131 field.value.get(self.head),
1132 ))
1133 }
1134
1135 #[inline]
1136 fn size_hint(&self) -> (usize, Option<usize>) {
1137 self.fields.size_hint()
1138 }
1139}
1140
1141impl ExactSizeIterator for Headers<'_> {}
1142
1143/// Shows the fields as `(name, value)` pairs; the values of credential
1144/// fields (`authorization`, `proxy-authorization`, `cookie`, `set-cookie`)
1145/// are replaced by `<redacted N bytes>`.
1146impl fmt::Debug for Headers<'_> {
1147 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1148 let head = self.head;
1149 f.debug_list()
1150 .entries(self.fields.clone().map(|field| {
1151 let name = field.name.get(head);
1152 (
1153 FieldName(name),
1154 FieldValue {
1155 name,
1156 value: field.value.get(head),
1157 },
1158 )
1159 }))
1160 .finish()
1161 }
1162}
1163
1164/// Iterator over the values of every field with one name, in arrival
1165/// order: what [`RequestHead::header_all`] and
1166/// [`ResponseHead::header_all`] return. Each `Set-Cookie`, for example, is
1167/// its own field and its own item; nothing is comma-joined.
1168///
1169/// # Examples
1170///
1171/// ```
1172/// use http_net::{ServerConn, ServerEvent};
1173///
1174/// let mut conn = ServerConn::new();
1175/// let _ = conn.feed(b"GET / HTTP/1.1\r\nHost: a\r\nVia: 1.1 p1\r\nVia: 1.1 p2\r\n\r\n");
1176/// let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1177/// let vias: Vec<&[u8]> = req.header_all("via").collect();
1178/// assert_eq!(vias, [&b"1.1 p1"[..], &b"1.1 p2"[..]]);
1179/// ```
1180///
1181/// The values borrow the head (`'a`), not the name (`'n`), so they outlive
1182/// a temporary name used for the lookup.
1183#[derive(Clone)]
1184pub struct HeaderValues<'a, 'n> {
1185 head: &'a [u8],
1186 fields: slice::Iter<'a, Field>,
1187 name: &'n [u8],
1188}
1189
1190impl<'a> Iterator for HeaderValues<'a, '_> {
1191 type Item = &'a [u8];
1192
1193 #[inline]
1194 fn next(&mut self) -> Option<Self::Item> {
1195 let (head, name) = (self.head, self.name);
1196 self.fields
1197 .find(|f| f.name.get(head).eq_ignore_ascii_case(name))
1198 .map(|f| f.value.get(head))
1199 }
1200
1201 #[inline]
1202 fn size_hint(&self) -> (usize, Option<usize>) {
1203 (0, self.fields.size_hint().1)
1204 }
1205}
1206
1207impl fmt::Debug for HeaderValues<'_, '_> {
1208 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1209 let name = self.name;
1210 f.debug_list()
1211 .entries(self.clone().map(|value| FieldValue { name, value }))
1212 .finish()
1213 }
1214}
1215
1216/// Iterator over the elements of a list field (`#rule`) across every line
1217/// with one name: what [`RequestHead::header_list`],
1218/// [`ResponseHead::header_list`], [`RequestHead::upgrade_protocols`], and
1219/// [`RequestHead::connection_options`] return. Elements are trimmed of whitespace, empty ones are skipped, and
1220/// a comma inside a quoted string does not split an element.
1221///
1222/// # Examples
1223///
1224/// ```
1225/// use http_net::{ServerConn, ServerEvent};
1226///
1227/// let mut conn = ServerConn::new();
1228/// let _ = conn.feed(b"GET / HTTP/1.1\r\nHost: a\r\nConnection: keep-alive, X-Trace\r\n\r\n");
1229/// let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1230/// let options: Vec<&[u8]> = req.header_list("connection").collect();
1231/// assert_eq!(options, [&b"keep-alive"[..], b"X-Trace"]);
1232/// ```
1233#[derive(Clone)]
1234pub struct ListElements<'a, 'n> {
1235 head: &'a [u8],
1236 fields: slice::Iter<'a, Field>,
1237 name: &'n [u8],
1238 current: ListSplit<'a>,
1239 skip_h2c: bool,
1240}
1241
1242impl<'a> Iterator for ListElements<'a, '_> {
1243 type Item = &'a [u8];
1244
1245 fn next(&mut self) -> Option<Self::Item> {
1246 loop {
1247 if let Some(e) = self.current.next() {
1248 if self.skip_h2c && is_h2c(e) {
1249 continue;
1250 }
1251 return Some(e);
1252 }
1253 let (head, name) = (self.head, self.name);
1254 let field = self
1255 .fields
1256 .find(|f| f.name.get(head).eq_ignore_ascii_case(name))?;
1257 self.current = list_elements(field.value.get(head));
1258 }
1259 }
1260}
1261
1262impl fmt::Debug for ListElements<'_, '_> {
1263 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1264 let name = self.name;
1265 f.debug_list()
1266 .entries(self.clone().map(|value| FieldValue { name, value }))
1267 .finish()
1268 }
1269}
1270
1271fn list_values<'a, 'n>(head: &'a [u8], fields: &'a [Field], name: &'n str) -> ListElements<'a, 'n> {
1272 ListElements {
1273 head,
1274 fields: fields.iter(),
1275 name: name.as_bytes(),
1276 current: list_elements(&[]),
1277 skip_h2c: false,
1278 }
1279}
1280
1281fn find_header<'a>(head: &'a [u8], fields: &[Field], name: &str) -> Option<&'a [u8]> {
1282 fields
1283 .iter()
1284 .find(|f| f.name.get(head).eq_ignore_ascii_case(name.as_bytes()))
1285 .map(|f| f.value.get(head))
1286}
1287
1288fn header_values<'a, 'n>(
1289 head: &'a [u8],
1290 fields: &'a [Field],
1291 name: &'n str,
1292) -> HeaderValues<'a, 'n> {
1293 HeaderValues {
1294 head,
1295 fields: fields.iter(),
1296 name: name.as_bytes(),
1297 }
1298}
1299
1300/// A parsed request head, borrowed from the connection's buffer.
1301///
1302/// Every accessor is a slice of the bytes that arrived; nothing is copied.
1303/// The view stays valid until the next call that mutates the connection.
1304/// Read it again later with [`ServerConn::request`](crate::ServerConn::request).
1305///
1306/// # Examples
1307///
1308/// ```
1309/// use http_net::{Method, ServerConn, ServerEvent, Version};
1310///
1311/// let mut conn = ServerConn::new();
1312/// let _ = conn.feed(b"GET /search?q=rust HTTP/1.1\r\nHost: example.com\r\n\r\n");
1313/// let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1314/// assert_eq!(req.method(), Method::Get);
1315/// assert_eq!(req.path(), "/search");
1316/// assert_eq!(req.query(), Some("q=rust"));
1317/// assert_eq!(req.version(), Version::Http11);
1318/// assert_eq!(req.host(), Some("example.com"));
1319/// assert!(req.keep_alive());
1320/// ```
1321#[derive(Clone, Copy)]
1322pub struct RequestHead<'a> {
1323 head: &'a [u8],
1324 fields: &'a [Field],
1325 info: &'a RequestInfo,
1326}
1327
1328impl<'a> RequestHead<'a> {
1329 pub(crate) const fn new(head: &'a [u8], fields: &'a [Field], info: &'a RequestInfo) -> Self {
1330 Self { head, fields, info }
1331 }
1332
1333 /// The bytes the spans index (the head, possibly followed by more
1334 /// input), the field records, and the interpreted facts.
1335 pub(crate) const fn parts(&self) -> (&'a [u8], &'a [Field], &'a RequestInfo) {
1336 (self.head, self.fields, self.info)
1337 }
1338
1339 /// The method, classified.
1340 ///
1341 /// # Examples
1342 ///
1343 /// ```
1344 /// # use http_net::{Method, ServerConn, ServerEvent};
1345 /// # let mut conn = ServerConn::new();
1346 /// # let _ = conn.feed(b"DELETE /x HTTP/1.1\r\nHost: a\r\n\r\n");
1347 /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1348 /// assert_eq!(req.method(), Method::Delete);
1349 /// ```
1350 #[must_use]
1351 pub fn method(&self) -> Method {
1352 self.info.method
1353 }
1354
1355 /// The method exactly as received.
1356 ///
1357 /// # Examples
1358 ///
1359 /// ```
1360 /// # use http_net::{Method, ServerConn, ServerEvent};
1361 /// # let mut conn = ServerConn::new();
1362 /// # let _ = conn.feed(b"PROPFIND /x HTTP/1.1\r\nHost: a\r\n\r\n");
1363 /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1364 /// assert_eq!(req.method(), Method::Other);
1365 /// assert_eq!(req.method_str(), "PROPFIND");
1366 /// ```
1367 #[must_use]
1368 pub fn method_str(&self) -> &'a str {
1369 core::str::from_utf8(self.info.method_span.get(self.head)).unwrap_or("")
1370 }
1371
1372 /// The request target exactly as received (origin form `/path?query`,
1373 /// absolute form, authority form for `CONNECT`, or `*`).
1374 ///
1375 /// # Examples
1376 ///
1377 /// ```
1378 /// # use http_net::{ServerConn, ServerEvent};
1379 /// # let mut conn = ServerConn::new();
1380 /// # let _ = conn.feed(b"GET /a%20b?x=1 HTTP/1.1\r\nHost: a\r\n\r\n");
1381 /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1382 /// assert_eq!(req.target(), "/a%20b?x=1");
1383 /// ```
1384 #[must_use]
1385 pub fn target(&self) -> &'a str {
1386 core::str::from_utf8(self.info.target.get(self.head)).unwrap_or("")
1387 }
1388
1389 /// Which of the four RFC 9112 section 3.2 forms the target uses.
1390 ///
1391 /// # Examples
1392 ///
1393 /// ```
1394 /// # use http_net::{ServerConn, ServerEvent, TargetForm};
1395 /// # let mut conn = ServerConn::new();
1396 /// # let _ = conn.feed(b"GET http://a.test/x HTTP/1.1\r\nHost: a.test\r\n\r\n");
1397 /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1398 /// assert_eq!(req.target_form(), TargetForm::Absolute);
1399 /// ```
1400 #[must_use]
1401 pub fn target_form(&self) -> TargetForm {
1402 self.info.target_form
1403 }
1404
1405 /// The path: the target up to (not including) the first `?`, with
1406 /// percent-encoding left as received.
1407 ///
1408 /// For an absolute-form target (`http://host/p?q`) this is the path
1409 /// after the authority (`/p`), and `/` when that path is empty, so a
1410 /// router sees the same path whichever form the client used. For `*`
1411 /// and for `CONNECT`'s authority form it is the whole target.
1412 ///
1413 /// # Examples
1414 ///
1415 /// ```
1416 /// # use http_net::{ServerConn, ServerEvent};
1417 /// # let mut conn = ServerConn::new();
1418 /// # let _ = conn.feed(b"GET /files/a.txt?v=2 HTTP/1.1\r\nHost: a\r\n\r\n");
1419 /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1420 /// assert_eq!(req.path(), "/files/a.txt");
1421 ///
1422 /// # let mut conn = ServerConn::new();
1423 /// # let _ = conn.feed(b"GET http://a/files/b.txt HTTP/1.1\r\nHost: a\r\n\r\n");
1424 /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1425 /// assert_eq!(req.path(), "/files/b.txt");
1426 /// ```
1427 #[must_use]
1428 pub fn path(&self) -> &'a str {
1429 let origin = self.origin();
1430 let path = origin.split_once('?').map_or(origin, |(p, _)| p);
1431 if path.is_empty() && self.info.authority.is_some() {
1432 // RFC 9110 section 4.2.3: an empty path in an http URI is "/".
1433 "/"
1434 } else {
1435 path
1436 }
1437 }
1438
1439 /// The query after the first `?`, if there is one.
1440 ///
1441 /// # Examples
1442 ///
1443 /// ```
1444 /// # use http_net::{ServerConn, ServerEvent};
1445 /// # let mut conn = ServerConn::new();
1446 /// # let _ = conn.feed(b"GET /x HTTP/1.1\r\nHost: a\r\n\r\n");
1447 /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1448 /// assert_eq!(req.query(), None);
1449 /// ```
1450 #[must_use]
1451 pub fn query(&self) -> Option<&'a str> {
1452 self.origin().split_once('?').map(|(_, q)| q)
1453 }
1454
1455 /// The path-and-query part of the target.
1456 fn origin(&self) -> &'a str {
1457 core::str::from_utf8(self.info.origin.get(self.head)).unwrap_or("")
1458 }
1459
1460 /// The protocol version.
1461 ///
1462 /// # Examples
1463 ///
1464 /// ```
1465 /// # use http_net::{ServerConn, ServerEvent, Version};
1466 /// # let mut conn = ServerConn::new();
1467 /// # let _ = conn.feed(b"GET /x HTTP/1.0\r\n\r\n");
1468 /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1469 /// assert_eq!(req.version(), Version::Http10);
1470 /// ```
1471 #[must_use]
1472 pub fn version(&self) -> Version {
1473 self.info.version
1474 }
1475
1476 /// All fields in arrival order.
1477 ///
1478 /// # Examples
1479 ///
1480 /// ```
1481 /// # use http_net::{ServerConn, ServerEvent};
1482 /// # let mut conn = ServerConn::new();
1483 /// # let _ = conn.feed(b"GET /x HTTP/1.1\r\nHost: a\r\nAccept: */*\r\n\r\n");
1484 /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1485 /// assert_eq!(req.headers().len(), 2);
1486 /// ```
1487 #[must_use]
1488 pub fn headers(&self) -> Headers<'a> {
1489 Headers {
1490 head: self.head,
1491 fields: self.fields.iter(),
1492 }
1493 }
1494
1495 /// The value of the first field named `name` (ASCII case-insensitive).
1496 ///
1497 /// # Examples
1498 ///
1499 /// ```
1500 /// # use http_net::{ServerConn, ServerEvent};
1501 /// # let mut conn = ServerConn::new();
1502 /// # let _ = conn.feed(b"GET /x HTTP/1.1\r\nHost: a\r\nAccept: text/html \r\n\r\n");
1503 /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1504 /// assert_eq!(req.header("accept"), Some(&b"text/html"[..]));
1505 /// assert_eq!(req.header("cookie"), None);
1506 /// ```
1507 #[must_use]
1508 pub fn header(&self, name: &str) -> Option<&'a [u8]> {
1509 find_header(self.head, self.fields, name)
1510 }
1511
1512 /// Like [`header`](Self::header), but only when the value is UTF-8.
1513 ///
1514 /// # Examples
1515 ///
1516 /// ```
1517 /// # use http_net::{ServerConn, ServerEvent};
1518 /// # let mut conn = ServerConn::new();
1519 /// # let _ = conn.feed(b"GET /x HTTP/1.1\r\nHost: a\r\nX: \xff\r\n\r\n");
1520 /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1521 /// assert_eq!(req.header_str("host"), Some("a"));
1522 /// assert_eq!(req.header_str("x"), None);
1523 /// ```
1524 #[must_use]
1525 pub fn header_str(&self, name: &str) -> Option<&'a str> {
1526 self.header(name).and_then(|v| core::str::from_utf8(v).ok())
1527 }
1528
1529 /// The values of every field named `name` (ASCII case-insensitive), in
1530 /// arrival order: for fields that may be repeated, such as `via`,
1531 /// `forwarded`, or `cookie` from clients that send one line per cookie.
1532 ///
1533 /// # Examples
1534 ///
1535 /// ```
1536 /// # use http_net::{ServerConn, ServerEvent};
1537 /// # let mut conn = ServerConn::new();
1538 /// # let _ = conn.feed(b"GET /x HTTP/1.1\r\nHost: a\r\nCookie: a=1\r\nCookie: b=2\r\n\r\n");
1539 /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1540 /// let cookies: Vec<&[u8]> = req.header_all("cookie").collect();
1541 /// assert_eq!(cookies, [&b"a=1"[..], &b"b=2"[..]]);
1542 /// assert_eq!(req.header_all("accept").count(), 0);
1543 ///
1544 /// // The values borrow the head, not the name.
1545 /// let first = {
1546 /// let name = String::from("COOKIE");
1547 /// req.header_all(&name).next()
1548 /// };
1549 /// assert_eq!(first, Some(&b"a=1"[..]));
1550 /// ```
1551 #[must_use]
1552 pub fn header_all<'n>(&self, name: &'n str) -> HeaderValues<'a, 'n> {
1553 header_values(self.head, self.fields, name)
1554 }
1555
1556 /// The declared `Content-Length`, if the request has one. Unlike
1557 /// [`framing`](Self::framing), which reports `Content-Length: 0` as
1558 /// [`BodyFraming::None`], this tells `Some(0)` from an absent field.
1559 ///
1560 /// # Examples
1561 ///
1562 /// ```
1563 /// # use http_net::{BodyFraming, ServerConn, ServerEvent};
1564 /// # let mut conn = ServerConn::new();
1565 /// # let _ = conn.feed(b"POST /x HTTP/1.1\r\nHost: a\r\nContent-Length: 0\r\n\r\n");
1566 /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1567 /// assert_eq!(req.framing(), BodyFraming::None);
1568 /// assert_eq!(req.content_length(), Some(0));
1569 /// ```
1570 #[must_use]
1571 pub fn content_length(&self) -> Option<u64> {
1572 self.info.content_length
1573 }
1574
1575 /// The authority the request is for (`host [":" port]`), if known.
1576 ///
1577 /// This is the `Host` field, except for an absolute-form target
1578 /// (`GET http://example.com/ HTTP/1.1`), whose authority wins: RFC 9112
1579 /// section 3.2.2 requires a server to ignore `Host` then, so the two can
1580 /// never be read differently. The raw field stays available through
1581 /// `header("host")`. HTTP/1.1 requests always carry a valid `Host`;
1582 /// HTTP/1.0 requests may not, and an empty `Host` (a target with no
1583 /// authority) is reported as `Some("")`.
1584 ///
1585 /// # Examples
1586 ///
1587 /// ```
1588 /// # use http_net::{ServerConn, ServerEvent};
1589 /// # let mut conn = ServerConn::new();
1590 /// # let _ = conn.feed(b"GET /x HTTP/1.1\r\nHost: 127.0.0.1:8080\r\n\r\n");
1591 /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1592 /// assert_eq!(req.host(), Some("127.0.0.1:8080"));
1593 ///
1594 /// # let mut conn = ServerConn::new();
1595 /// # let _ = conn.feed(b"GET http://a.test/x HTTP/1.1\r\nHost: b.test\r\n\r\n");
1596 /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1597 /// assert_eq!(req.host(), Some("a.test"));
1598 /// assert_eq!(req.header("host"), Some(&b"b.test"[..]));
1599 /// ```
1600 #[must_use]
1601 pub fn host(&self) -> Option<&'a str> {
1602 self.info
1603 .authority
1604 .or(self.info.host)
1605 .and_then(|s| core::str::from_utf8(s.get(self.head)).ok())
1606 }
1607
1608 /// Whether the client sent `Expect: 100-continue` and is waiting for a
1609 /// `100 Continue` before sending the body. Only HTTP/1.1 requests that
1610 /// have a body report it (RFC 9110 section 10.1.1).
1611 ///
1612 /// Answer it before reading the body: either
1613 /// [`ServerConn::send_continue`](crate::ServerConn::send_continue), or a
1614 /// final response straight away (which closes the connection afterwards,
1615 /// since the body was not read). The blocking `Server` sends
1616 /// `100 Continue` itself.
1617 ///
1618 /// # Examples
1619 ///
1620 /// ```
1621 /// # use http_net::{ServerConn, ServerEvent};
1622 /// # let mut conn = ServerConn::new();
1623 /// # let _ = conn.feed(b"PUT /f HTTP/1.1\r\nHost: a\r\nExpect: 100-continue\r\nContent-Length: 9\r\n\r\n");
1624 /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1625 /// assert!(req.expects_continue());
1626 /// ```
1627 #[must_use]
1628 pub fn expects_continue(&self) -> bool {
1629 self.info.expect_continue
1630 }
1631
1632 /// How the request body is delimited.
1633 ///
1634 /// # Examples
1635 ///
1636 /// ```
1637 /// # use http_net::{BodyFraming, ServerConn, ServerEvent};
1638 /// # let mut conn = ServerConn::new();
1639 /// # let _ = conn.feed(b"POST /x HTTP/1.1\r\nHost: a\r\nTransfer-Encoding: chunked\r\n\r\n");
1640 /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1641 /// assert_eq!(req.framing(), BodyFraming::Chunked);
1642 /// ```
1643 #[must_use]
1644 pub fn framing(&self) -> BodyFraming {
1645 self.info.framing
1646 }
1647
1648 /// Whether the client allows the connection to stay open after this
1649 /// exchange (HTTP/1.1 unless `Connection: close`; HTTP/1.0 only with
1650 /// `Connection: keep-alive`).
1651 ///
1652 /// # Examples
1653 ///
1654 /// ```
1655 /// # use http_net::{ServerConn, ServerEvent};
1656 /// # let mut conn = ServerConn::new();
1657 /// # let _ = conn.feed(b"GET /x HTTP/1.1\r\nHost: a\r\nConnection: close\r\n\r\n");
1658 /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1659 /// assert!(!req.keep_alive());
1660 /// ```
1661 #[must_use]
1662 pub fn keep_alive(&self) -> bool {
1663 self.info.keep_alive
1664 }
1665
1666 /// Whether this is an HTTP/1.1 request to switch protocols: `Connection`
1667 /// lists `upgrade` and an `Upgrade` field offers at least one protocol
1668 /// other than `h2c` (read the offers with
1669 /// [`upgrade_protocols`](Self::upgrade_protocols)). Answer
1670 /// with [`Response::switching_protocols`](crate::Response::switching_protocols)
1671 /// naming an offered protocol to hand the connection over.
1672 ///
1673 /// `h2c` is never an offer: RFC 9113 section 3.1 deprecates the
1674 /// HTTP/2 upgrade, and a proxy that forwards it blindly is open to h2c
1675 /// smuggling. A request offering only `h2c` is served as HTTP/1.1.
1676 ///
1677 /// # Examples
1678 ///
1679 /// ```
1680 /// # use http_net::{ServerConn, ServerEvent};
1681 /// # let mut conn = ServerConn::new();
1682 /// # let _ = conn.feed(b"GET /ws HTTP/1.1\r\nHost: a\r\nConnection: Upgrade\r\nUpgrade: websocket\r\n\r\n");
1683 /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1684 /// assert!(req.is_upgrade());
1685 /// assert_eq!(req.header("upgrade"), Some(&b"websocket"[..]));
1686 ///
1687 /// # let mut conn = ServerConn::new();
1688 /// # let _ = conn.feed(b"GET / HTTP/1.1\r\nHost: a\r\nConnection: Upgrade, HTTP2-Settings\r\nUpgrade: h2c\r\nHTTP2-Settings: AAMAAABkAAQAoAAA\r\n\r\n");
1689 /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1690 /// assert!(!req.is_upgrade());
1691 /// ```
1692 #[must_use]
1693 pub fn is_upgrade(&self) -> bool {
1694 self.info.upgrade
1695 }
1696
1697 /// Whether the request carries an expectation other than
1698 /// `100-continue` (any `Expect` list element, any HTTP version), which
1699 /// a server may answer with `417 Expectation Failed` (RFC 9110 section
1700 /// 10.1.1). The core never answers it by itself; the blocking server
1701 /// does. The expectation never changes how the body is framed.
1702 ///
1703 /// # Examples
1704 ///
1705 /// ```
1706 /// # use http_net::{ServerConn, ServerEvent};
1707 /// # let mut conn = ServerConn::new();
1708 /// # let _ = conn.feed(b"PUT /f HTTP/1.1\r\nHost: a\r\nExpect: 100-continue, x-fast\r\nContent-Length: 1\r\n\r\n");
1709 /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1710 /// assert!(req.expects_continue());
1711 /// assert!(req.has_unknown_expectation());
1712 /// ```
1713 #[must_use]
1714 pub fn has_unknown_expectation(&self) -> bool {
1715 self.info.unknown_expectation
1716 }
1717
1718 /// The protocols the request's `Upgrade` field offers, in order, as
1719 /// product tokens (`websocket`, `foo/2`), across every `Upgrade` line;
1720 /// `h2c` is left out (it is never an offer). Empty unless
1721 /// [`is_upgrade`](Self::is_upgrade).
1722 ///
1723 /// # Examples
1724 ///
1725 /// ```
1726 /// # use http_net::{ServerConn, ServerEvent};
1727 /// # let mut conn = ServerConn::new();
1728 /// # let _ = conn.feed(b"GET / HTTP/1.1\r\nHost: a\r\nConnection: upgrade\r\nUpgrade: h2c, websocket/13, irc\r\n\r\n");
1729 /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1730 /// let offered: Vec<&[u8]> = req.upgrade_protocols().collect();
1731 /// assert_eq!(offered, [&b"websocket/13"[..], b"irc"]);
1732 /// ```
1733 #[must_use]
1734 pub fn upgrade_protocols(&self) -> ListElements<'a, 'static> {
1735 let mut list = list_values(self.head, self.fields, "upgrade");
1736 list.skip_h2c = true;
1737 if !self.info.upgrade {
1738 list.fields = [].iter();
1739 }
1740 list
1741 }
1742
1743 /// The options the request's `Connection` field lists, across every
1744 /// `Connection` line, in order (`close`, `keep-alive`, `upgrade`, and the
1745 /// names of fields that are hop-by-hop for this message). Compare them
1746 /// ASCII case-insensitively. A proxy passes them to
1747 /// [`is_hop_by_hop`](crate::is_hop_by_hop).
1748 ///
1749 /// # Examples
1750 ///
1751 /// ```
1752 /// # use http_net::{ServerConn, ServerEvent};
1753 /// # let mut conn = ServerConn::new();
1754 /// # let _ = conn.feed(b"GET / HTTP/1.1\r\nHost: a\r\nConnection: keep-alive, X-Trace\r\n\r\n");
1755 /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1756 /// let options: Vec<&[u8]> = req.connection_options().collect();
1757 /// assert_eq!(options, [&b"keep-alive"[..], b"X-Trace"]);
1758 /// ```
1759 #[must_use]
1760 pub fn connection_options(&self) -> ListElements<'a, 'static> {
1761 list_values(self.head, self.fields, "connection")
1762 }
1763
1764 /// The elements of a comma-separated list field (`#rule`, RFC 9110
1765 /// section 5.6.1) across every line with this name, in order, trimmed
1766 /// of whitespace, empty elements skipped. A comma inside a quoted
1767 /// string does not split an element.
1768 ///
1769 /// # Examples
1770 ///
1771 /// ```
1772 /// # use http_net::{ServerConn, ServerEvent};
1773 /// # let mut conn = ServerConn::new();
1774 /// # let _ = conn.feed(b"GET / HTTP/1.1\r\nHost: a\r\nAccept: text/html, ,\r\nAccept: x/y;q=\"0.5,1\"\r\n\r\n");
1775 /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1776 /// let accept: Vec<&[u8]> = req.header_list("accept").collect();
1777 /// assert_eq!(accept, [&b"text/html"[..], b"x/y;q=\"0.5,1\""]);
1778 /// ```
1779 #[must_use]
1780 pub fn header_list<'n>(&self, name: &'n str) -> ListElements<'a, 'n> {
1781 list_values(self.head, self.fields, name)
1782 }
1783
1784 /// Whether the request's `Upgrade` field offers `protocol` (see
1785 /// [`Response::switching_protocols`](crate::Response::switching_protocols)).
1786 pub(crate) fn offers(&self, protocol: &[u8]) -> bool {
1787 self.info.upgrade
1788 && self
1789 .header_all("upgrade")
1790 .any(|v| list_elements(v).any(|e| protocol_matches(protocol, e)))
1791 }
1792}
1793
1794/// Shows method, target, version, fields, and framing; the query and the
1795/// values of credential fields are replaced by `<redacted N bytes>`.
1796impl fmt::Debug for RequestHead<'_> {
1797 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1798 f.debug_struct("RequestHead")
1799 .field("method", &self.method_str())
1800 .field("target", &Target(self.target()))
1801 .field("version", &self.version())
1802 .field("headers", &self.headers())
1803 .field("framing", &self.framing())
1804 .finish()
1805 }
1806}
1807
1808/// A parsed response head, borrowed from the client connection's buffer.
1809///
1810/// # Examples
1811///
1812/// ```
1813/// use http_net::{ClientConn, ClientEvent, Request};
1814///
1815/// let mut conn = ClientConn::new();
1816/// let mut out = Vec::new();
1817/// conn.send_request(&Request::get("/").header("host", "a"), &mut out).unwrap();
1818/// let _ = conn.feed(b"HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n");
1819/// let ClientEvent::Response(resp) = conn.poll().unwrap() else { panic!() };
1820/// assert_eq!(resp.status(), 404);
1821/// assert_eq!(resp.reason(), "Not Found");
1822/// ```
1823#[derive(Clone, Copy)]
1824pub struct ResponseHead<'a> {
1825 head: &'a [u8],
1826 fields: &'a [Field],
1827 info: &'a ResponseInfo,
1828}
1829
1830impl<'a> ResponseHead<'a> {
1831 pub(crate) const fn new(head: &'a [u8], fields: &'a [Field], info: &'a ResponseInfo) -> Self {
1832 Self { head, fields, info }
1833 }
1834
1835 /// As [`RequestHead::parts`], for a response.
1836 pub(crate) const fn parts(&self) -> (&'a [u8], &'a [Field], &'a ResponseInfo) {
1837 (self.head, self.fields, self.info)
1838 }
1839
1840 /// The status code (`100..=599`).
1841 ///
1842 /// # Examples
1843 ///
1844 /// ```
1845 /// # use http_net::{ClientConn, ClientEvent, Request};
1846 /// # let mut conn = ClientConn::new();
1847 /// # conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new()).unwrap();
1848 /// # let _ = conn.feed(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
1849 /// # let ClientEvent::Response(resp) = conn.poll().unwrap() else { panic!() };
1850 /// assert_eq!(resp.status(), 200);
1851 /// ```
1852 #[must_use]
1853 pub fn status(&self) -> u16 {
1854 self.info.status
1855 }
1856
1857 /// The reason phrase, if it is UTF-8 (it carries no meaning; RFC 9112
1858 /// tells clients to ignore it). Empty for a status line without one,
1859 /// including `HTTP/1.1 200` with no space after the code.
1860 ///
1861 /// # Examples
1862 ///
1863 /// ```
1864 /// # use http_net::{ClientConn, ClientEvent, Request};
1865 /// # let mut conn = ClientConn::new();
1866 /// # conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new()).unwrap();
1867 /// # let _ = conn.feed(b"HTTP/1.1 200 \r\nContent-Length: 0\r\n\r\n");
1868 /// # let ClientEvent::Response(resp) = conn.poll().unwrap() else { panic!() };
1869 /// assert_eq!(resp.reason(), "");
1870 /// ```
1871 #[must_use]
1872 pub fn reason(&self) -> &'a str {
1873 core::str::from_utf8(self.info.reason.get(self.head)).unwrap_or("")
1874 }
1875
1876 /// The protocol version.
1877 ///
1878 /// # Examples
1879 ///
1880 /// ```
1881 /// # use http_net::{ClientConn, ClientEvent, Request, Version};
1882 /// # let mut conn = ClientConn::new();
1883 /// # conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new()).unwrap();
1884 /// # let _ = conn.feed(b"HTTP/1.0 200 OK\r\n\r\n");
1885 /// # let ClientEvent::Response(resp) = conn.poll().unwrap() else { panic!() };
1886 /// assert_eq!(resp.version(), Version::Http10);
1887 /// ```
1888 #[must_use]
1889 pub fn version(&self) -> Version {
1890 self.info.version
1891 }
1892
1893 /// All fields in arrival order.
1894 ///
1895 /// # Examples
1896 ///
1897 /// ```
1898 /// # use http_net::{ClientConn, ClientEvent, Request};
1899 /// # let mut conn = ClientConn::new();
1900 /// # conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new()).unwrap();
1901 /// # let _ = conn.feed(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\nX: y\r\n\r\n");
1902 /// # let ClientEvent::Response(resp) = conn.poll().unwrap() else { panic!() };
1903 /// assert_eq!(resp.headers().count(), 2);
1904 /// ```
1905 #[must_use]
1906 pub fn headers(&self) -> Headers<'a> {
1907 Headers {
1908 head: self.head,
1909 fields: self.fields.iter(),
1910 }
1911 }
1912
1913 /// The value of the first field named `name` (ASCII case-insensitive).
1914 ///
1915 /// # Examples
1916 ///
1917 /// ```
1918 /// # use http_net::{ClientConn, ClientEvent, Request};
1919 /// # let mut conn = ClientConn::new();
1920 /// # conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new()).unwrap();
1921 /// # let _ = conn.feed(b"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 0\r\n\r\n");
1922 /// # let ClientEvent::Response(resp) = conn.poll().unwrap() else { panic!() };
1923 /// assert_eq!(resp.header("content-type"), Some(&b"text/plain"[..]));
1924 /// ```
1925 #[must_use]
1926 pub fn header(&self, name: &str) -> Option<&'a [u8]> {
1927 find_header(self.head, self.fields, name)
1928 }
1929
1930 /// Like [`header`](Self::header), but only when the value is UTF-8.
1931 ///
1932 /// # Examples
1933 ///
1934 /// ```
1935 /// # use http_net::{ClientConn, ClientEvent, Request};
1936 /// # let mut conn = ClientConn::new();
1937 /// # conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new()).unwrap();
1938 /// # let _ = conn.feed(b"HTTP/1.1 200 OK\r\nServer: x\r\nContent-Length: 0\r\n\r\n");
1939 /// # let ClientEvent::Response(resp) = conn.poll().unwrap() else { panic!() };
1940 /// assert_eq!(resp.header_str("server"), Some("x"));
1941 /// ```
1942 #[must_use]
1943 pub fn header_str(&self, name: &str) -> Option<&'a str> {
1944 self.header(name).and_then(|v| core::str::from_utf8(v).ok())
1945 }
1946
1947 /// The values of every field named `name`, in arrival order. Each
1948 /// `Set-Cookie` is its own field (they are never comma-joined), so
1949 /// this is how a client reads all of them.
1950 ///
1951 /// # Examples
1952 ///
1953 /// ```
1954 /// # use http_net::{ClientConn, ClientEvent, Request};
1955 /// # let mut conn = ClientConn::new();
1956 /// # conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new()).unwrap();
1957 /// # let _ = conn.feed(b"HTTP/1.1 200 OK\r\nSet-Cookie: a=1\r\nSet-Cookie: b=2; Path=/\r\nContent-Length: 0\r\n\r\n");
1958 /// # let ClientEvent::Response(resp) = conn.poll().unwrap() else { panic!() };
1959 /// let cookies: Vec<&[u8]> = resp.header_all("set-cookie").collect();
1960 /// assert_eq!(cookies, [&b"a=1"[..], &b"b=2; Path=/"[..]]);
1961 /// ```
1962 #[must_use]
1963 pub fn header_all<'n>(&self, name: &'n str) -> HeaderValues<'a, 'n> {
1964 header_values(self.head, self.fields, name)
1965 }
1966
1967 /// The declared `Content-Length`, if the response has one, whatever the
1968 /// framing rules made of it (a `HEAD` response's length is reported
1969 /// here while [`framing`](Self::framing) is `None`).
1970 ///
1971 /// # Examples
1972 ///
1973 /// ```
1974 /// # use http_net::{BodyFraming, ClientConn, ClientEvent, Request};
1975 /// # let mut conn = ClientConn::new();
1976 /// # conn.send_request(&Request::head("/").header("host", "a"), &mut Vec::new()).unwrap();
1977 /// # let _ = conn.feed(b"HTTP/1.1 200 OK\r\nContent-Length: 1234\r\n\r\n");
1978 /// # let ClientEvent::Response(resp) = conn.poll().unwrap() else { panic!() };
1979 /// assert_eq!(resp.framing(), BodyFraming::None);
1980 /// assert_eq!(resp.content_length(), Some(1234));
1981 /// ```
1982 #[must_use]
1983 pub fn content_length(&self) -> Option<u64> {
1984 self.info.content_length
1985 }
1986
1987 /// The elements of a list field across every line with this name (see
1988 /// [`RequestHead::header_list`]).
1989 ///
1990 /// # Examples
1991 ///
1992 /// ```
1993 /// # use http_net::{ClientConn, ClientEvent, Request};
1994 /// # let mut conn = ClientConn::new();
1995 /// # conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new()).unwrap();
1996 /// # let _ = conn.feed(b"HTTP/1.1 200 OK\r\nVary: accept\r\nVary: origin, cookie\r\nContent-Length: 0\r\n\r\n");
1997 /// # let ClientEvent::Response(resp) = conn.poll().unwrap() else { panic!() };
1998 /// assert_eq!(resp.header_list("vary").count(), 3);
1999 /// ```
2000 #[must_use]
2001 pub fn header_list<'n>(&self, name: &'n str) -> ListElements<'a, 'n> {
2002 list_values(self.head, self.fields, name)
2003 }
2004
2005 /// Whether this is an interim (1xx) response head, as reported by
2006 /// [`ClientEvent::Informational`](crate::ClientEvent::Informational).
2007 ///
2008 /// # Examples
2009 ///
2010 /// ```
2011 /// # use http_net::{ClientConn, ClientEvent, Request};
2012 /// # let mut conn = ClientConn::new();
2013 /// # conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new()).unwrap();
2014 /// # let _ = conn.feed(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
2015 /// # let ClientEvent::Response(resp) = conn.poll().unwrap() else { panic!() };
2016 /// assert!(!resp.is_informational());
2017 /// ```
2018 #[must_use]
2019 pub fn is_informational(&self) -> bool {
2020 (100..200).contains(&self.info.status)
2021 }
2022
2023 /// How the response body is delimited (accounts for `HEAD` requests and
2024 /// bodiless status codes).
2025 ///
2026 /// # Examples
2027 ///
2028 /// ```
2029 /// # use http_net::{BodyFraming, ClientConn, ClientEvent, Request};
2030 /// # let mut conn = ClientConn::new();
2031 /// # conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new()).unwrap();
2032 /// # let _ = conn.feed(b"HTTP/1.1 200 OK\r\n\r\n");
2033 /// # let ClientEvent::Response(resp) = conn.poll().unwrap() else { panic!() };
2034 /// assert_eq!(resp.framing(), BodyFraming::UntilClose);
2035 /// ```
2036 #[must_use]
2037 pub fn framing(&self) -> BodyFraming {
2038 self.info.framing
2039 }
2040
2041 /// Whether the connection can carry another request after this response.
2042 ///
2043 /// # Examples
2044 ///
2045 /// ```
2046 /// # use http_net::{ClientConn, ClientEvent, Request};
2047 /// # let mut conn = ClientConn::new();
2048 /// # conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new()).unwrap();
2049 /// # let _ = conn.feed(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
2050 /// # let ClientEvent::Response(resp) = conn.poll().unwrap() else { panic!() };
2051 /// assert!(resp.keep_alive());
2052 /// ```
2053 #[must_use]
2054 pub fn keep_alive(&self) -> bool {
2055 self.info.keep_alive
2056 }
2057}
2058
2059/// Shows status, reason, version, fields, and framing; the values of
2060/// credential fields (`set-cookie` above all) are replaced by
2061/// `<redacted N bytes>`.
2062impl fmt::Debug for ResponseHead<'_> {
2063 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2064 f.debug_struct("ResponseHead")
2065 .field("status", &self.status())
2066 .field("reason", &self.reason())
2067 .field("version", &self.version())
2068 .field("headers", &self.headers())
2069 .field("framing", &self.framing())
2070 .finish()
2071 }
2072}
2073
2074#[cfg(test)]
2075mod tests {
2076 use alloc::vec::Vec;
2077
2078 use super::*;
2079
2080 /// Lines built from the bytes that decide field parsing, at random.
2081 fn random_lines(count: usize) -> Vec<Vec<u8>> {
2082 const BYTES: &[u8] = b"aZ09-!~:: \t\r\n\x00\x7f\x80\xff\"(,";
2083 let mut state = 0x2545_f491_4f6c_dd1du64;
2084 let mut next = move || {
2085 state ^= state << 13;
2086 state ^= state >> 7;
2087 state ^= state << 17;
2088 state
2089 };
2090 // Mostly the shape a field line has (a name, a colon, a value), with
2091 // the deciding bytes mixed in now and then, so lines land on both
2092 // sides of what the fast path accepts.
2093 let mut pick = move |plain: &[u8]| {
2094 if next() % 12 == 0 {
2095 BYTES[(next() % BYTES.len() as u64) as usize]
2096 } else {
2097 plain[(next() % plain.len() as u64) as usize]
2098 }
2099 };
2100 (0..count)
2101 .map(|i| {
2102 let mut line = Vec::new();
2103 for _ in 0..(i % 9) {
2104 line.push(pick(b"abcXYZ019-_.!~"));
2105 }
2106 line.push(pick(b":"));
2107 for _ in 0..(i % 23) {
2108 line.push(pick(b" abc XYZ 019 ,;=/\"()[]{}"));
2109 }
2110 if i % 5 != 0 {
2111 line.extend_from_slice(b"\r\n");
2112 }
2113 line
2114 })
2115 .collect()
2116 }
2117
2118 #[test]
2119 fn test_fast_field_agrees_with_the_general_path() {
2120 let mut fast = 0;
2121 for line in random_lines(300_000).into_iter().chain([
2122 b"Host: a\r\n".to_vec(),
2123 b"X:\r\n".to_vec(),
2124 b"X: \r\n".to_vec(),
2125 b"X: a b \r\n".to_vec(),
2126 b"X: a\tb\r\n".to_vec(),
2127 b"X: a\r".to_vec(),
2128 b"X: a\n".to_vec(),
2129 b" X: a\r\n".to_vec(),
2130 b"X : a\r\n".to_vec(),
2131 b"X: \x80\r\n".to_vec(),
2132 b"X: a\rb\r\n".to_vec(),
2133 ]) {
2134 let Some((field, next)) = fast_field(&line, 0) else {
2135 continue;
2136 };
2137 fast += 1;
2138 // The general path on the same line: the LF it finds, then the
2139 // field it parses.
2140 let lf = find_byte(b'\n', &line).expect("an accepted line has an LF");
2141 assert_eq!(next, lf + 1, "{line:?}");
2142 assert_eq!(line[lf - 1], b'\r', "{line:?}");
2143 assert_eq!(parse_field(&line[..lf - 1], 0), Ok(field), "{line:?}");
2144 }
2145 assert!(fast > 1_000, "the fast path accepted only {fast} lines");
2146 }
2147
2148 #[test]
2149 fn test_scan_with_and_without_the_fast_path_agree_at_every_split() {
2150 // Whole input takes the fast path; one byte at a time cannot (a
2151 // line is never complete when its search starts), so it exercises
2152 // the general path. Both must report the same fields and errors.
2153 let heads: [&[u8]; 6] = [
2154 b"GET / HTTP/1.1\r\nHost: a\r\nX: b \r\nY:\r\n\r\n",
2155 b"GET / HTTP/1.1\r\nHost: a\r\nX: a\tb\r\n\r\n",
2156 b"GET / HTTP/1.1\r\nHost: a\r\nX: a\rb\r\n\r\n",
2157 b"GET / HTTP/1.1\r\nHost: a\r\nX : b\r\n\r\n",
2158 b"GET / HTTP/1.1\r\nHost: a\r\n X: b\r\n\r\n",
2159 b"GET / HTTP/1.1\r\nHost: a\r\nX: \x80\x81\r\n\r\n",
2160 ];
2161 let fields = |s: &HeadScanner| {
2162 s.fields
2163 .iter()
2164 .map(|f| (f.name, f.value))
2165 .collect::<Vec<_>>()
2166 };
2167 for head in heads {
2168 let mut whole = HeadScanner::new();
2169 let whole_result = whole.advance(head, &Limits::new(), HeadKind::Request, true);
2170 let mut split = HeadScanner::new();
2171 let mut split_result = Ok(Scan::Partial);
2172 for end in 1..=head.len() {
2173 split_result = split.advance(&head[..end], &Limits::new(), HeadKind::Request, true);
2174 if !matches!(split_result, Ok(Scan::Partial)) {
2175 break;
2176 }
2177 }
2178 assert_eq!(whole_result, split_result, "{head:?}");
2179 assert_eq!(fields(&whole), fields(&split), "{head:?}");
2180 }
2181 }
2182
2183 fn scan(msg: &[u8], kind: HeadKind) -> Result<(HeadScanner, usize), HttpError> {
2184 let mut s = HeadScanner::new();
2185 match s.advance(msg, &Limits::new(), kind, true)? {
2186 Scan::Done(n) => Ok((s, n)),
2187 Scan::Partial => Err(HttpError::UnexpectedEof),
2188 }
2189 }
2190
2191 #[test]
2192 fn test_scanner_resumes_without_rescanning() {
2193 let msg = b"GET / HTTP/1.1\r\nHost: a\r\n\r\n";
2194 let mut s = HeadScanner::new();
2195 for end in 1..msg.len() {
2196 assert!(matches!(
2197 s.advance(&msg[..end], &Limits::new(), HeadKind::Request, true),
2198 Ok(Scan::Partial)
2199 ));
2200 }
2201 assert!(matches!(
2202 s.advance(msg, &Limits::new(), HeadKind::Request, true),
2203 Ok(Scan::Done(n)) if n == msg.len()
2204 ));
2205 assert_eq!(s.fields.len(), 1);
2206 }
2207
2208 #[test]
2209 fn test_scanner_rejects_line_shapes() {
2210 let cases: &[(&[u8], HttpError)] = &[
2211 (b"GET / HTTP/1.1\nHost: a\r\n\r\n", HttpError::BareLf),
2212 (b"GET / HTTP/1.1\r\n Host: a\r\n\r\n", HttpError::ObsFold),
2213 (
2214 b"GET / HTTP/1.1\r\nHost : a\r\n\r\n",
2215 HttpError::WhitespaceBeforeColon,
2216 ),
2217 (
2218 b"GET / HTTP/1.1\r\nHost\r\n\r\n",
2219 HttpError::InvalidHeaderName,
2220 ),
2221 (
2222 b"GET / HTTP/1.1\r\n: a\r\n\r\n",
2223 HttpError::InvalidHeaderName,
2224 ),
2225 (b"GET / HTTP/1.1\r\nHo\rst: a\r\n\r\n", HttpError::BareCr),
2226 (b"GET / HTTP/1.1\r\nHost: a\rb\r\n\r\n", HttpError::BareCr),
2227 (b"GET /\r HTTP/1.1\r\n\r\n", HttpError::BareCr),
2228 (
2229 b"GET / HTTP/1.1\r\nHost: a\x00\r\n\r\n",
2230 HttpError::InvalidHeaderValue,
2231 ),
2232 ];
2233 for (msg, err) in cases {
2234 assert_eq!(scan(msg, HeadKind::Request).err(), Some(*err), "{msg:?}");
2235 }
2236 }
2237
2238 #[test]
2239 fn test_scanner_skips_leading_empty_lines_for_requests_only() {
2240 assert!(scan(b"\r\n\r\nGET / HTTP/1.1\r\n\r\n", HeadKind::Request).is_ok());
2241 assert_eq!(
2242 scan(b"\r\nHTTP/1.1 200 OK\r\n\r\n", HeadKind::Response).err(),
2243 Some(HttpError::InvalidStartLine)
2244 );
2245 }
2246
2247 #[test]
2248 fn test_scanner_enforces_limits_at_boundaries() {
2249 let limits = Limits::new().with_max_start_line(16);
2250 // "GET / HTTP/1.1\r\n" is exactly 16 bytes.
2251 let mut s = HeadScanner::new();
2252 assert!(
2253 s.advance(b"GET / HTTP/1.1\r\n\r\n", &limits, HeadKind::Request, true)
2254 .is_ok()
2255 );
2256 let mut s = HeadScanner::new();
2257 assert_eq!(
2258 s.advance(b"GET /a HTTP/1.1\r\n\r\n", &limits, HeadKind::Request, true)
2259 .err(),
2260 Some(HttpError::StartLineTooLong)
2261 );
2262 // Partial input already at the budget fails without waiting for LF.
2263 let mut s = HeadScanner::new();
2264 assert_eq!(
2265 s.advance(b"GET /aaaaaaaaaaaa", &limits, HeadKind::Request, true)
2266 .err(),
2267 Some(HttpError::StartLineTooLong)
2268 );
2269
2270 let limits = Limits::new().with_max_headers(1);
2271 let mut s = HeadScanner::new();
2272 assert_eq!(
2273 s.advance(
2274 b"GET / HTTP/1.1\r\nA: 1\r\nB: 2\r\n\r\n",
2275 &limits,
2276 HeadKind::Request,
2277 true
2278 )
2279 .err(),
2280 Some(HttpError::TooManyHeaders)
2281 );
2282
2283 // "A: 1\r\n" is 6 bytes; with 8 the final CRLF still fits.
2284 let limits = Limits::new().with_max_header_bytes(8);
2285 let mut s = HeadScanner::new();
2286 assert!(
2287 s.advance(
2288 b"GET / HTTP/1.1\r\nA: 1\r\n\r\n",
2289 &limits,
2290 HeadKind::Request,
2291 true
2292 )
2293 .is_ok()
2294 );
2295 let mut s = HeadScanner::new();
2296 assert_eq!(
2297 s.advance(
2298 b"GET / HTTP/1.1\r\nA: 12\r\nB: 1\r\n\r\n",
2299 &limits,
2300 HeadKind::Request,
2301 true
2302 )
2303 .err(),
2304 Some(HttpError::HeadersTooLarge)
2305 );
2306 }
2307
2308 fn response(line: &[u8]) -> Result<ResponseInfo, HttpError> {
2309 let mut msg = line.to_vec();
2310 msg.extend_from_slice(b"\r\nContent-Length: 0\r\n\r\n");
2311 let (s, _) = scan(&msg, HeadKind::Response)?;
2312 interpret_response(
2313 &msg,
2314 s.start_line(),
2315 &s.fields,
2316 &Limits::new(),
2317 Asked {
2318 head: false,
2319 connect: false,
2320 },
2321 )
2322 }
2323
2324 #[test]
2325 fn test_status_line_without_space_after_code_is_accepted() {
2326 let info = response(b"HTTP/1.1 200").unwrap();
2327 assert_eq!(info.status, 200);
2328 assert_eq!(info.reason, Span::new(12, 12));
2329 assert_eq!(response(b"HTTP/1.1 200 ").unwrap().status, 200);
2330 assert_eq!(response(b"HTTP/1.1 204 No Content").unwrap().status, 204);
2331 assert_eq!(
2332 response(b"HTTP/1.1 20").err(),
2333 Some(HttpError::InvalidStartLine)
2334 );
2335 assert_eq!(
2336 response(b"HTTP/1.1 2000").err(),
2337 Some(HttpError::InvalidStartLine)
2338 );
2339 assert_eq!(
2340 response(b"HTTP/1.1200 OK").err(),
2341 Some(HttpError::InvalidStartLine)
2342 );
2343 assert_eq!(
2344 response(b"HTTP/1.1 600").err(),
2345 Some(HttpError::InvalidStatus)
2346 );
2347 }
2348
2349 #[test]
2350 fn test_protocol_matching_rules() {
2351 assert!(protocol_matches(b"websocket", b"websocket"));
2352 assert!(protocol_matches(b"WebSocket", b"websocket"));
2353 assert!(protocol_matches(b"websocket", b"websocket/13"));
2354 assert!(protocol_matches(b"foo/2", b"FOO/2"));
2355 assert!(!protocol_matches(b"foo/2", b"foo/3"));
2356 assert!(!protocol_matches(b"foo/2", b"foo"));
2357 assert!(!protocol_matches(b"ws", b"websocket"));
2358 assert!(!protocol_matches(b"h2c", b"h2c"));
2359 assert!(!protocol_matches(b"H2C", b"h2c"));
2360 assert!(is_h2c(b"h2c/1") && is_h2c(b"H2c") && !is_h2c(b"h2"));
2361 }
2362
2363 #[test]
2364 fn test_authority_targets_refuse_empty_host_and_bad_escapes() {
2365 assert!(check_target(Method::Connect, b"a%41:443").is_ok());
2366 assert!(check_target(Method::Connect, b"a%4:443").is_err());
2367 assert!(check_target(Method::Get, b"http://a%zz/").is_err());
2368 assert!(check_target(Method::Get, b"http://a%41b/").is_ok());
2369 assert!(check_target(Method::Get, b"ftp://h/").is_ok());
2370 }
2371
2372 #[test]
2373 fn test_versions() {
2374 assert_eq!(parse_version(b"HTTP/1.1"), Ok(Version::Http11));
2375 assert_eq!(parse_version(b"HTTP/1.0"), Ok(Version::Http10));
2376 assert_eq!(parse_version(b"HTTP/1.9"), Ok(Version::Http11));
2377 assert_eq!(
2378 parse_version(b"HTTP/2.0"),
2379 Err(HttpError::UnsupportedVersion)
2380 );
2381 assert_eq!(
2382 parse_version(b"HTTP/0.9"),
2383 Err(HttpError::UnsupportedVersion)
2384 );
2385 assert_eq!(parse_version(b"http/1.1"), Err(HttpError::InvalidVersion));
2386 assert_eq!(parse_version(b"HTTP/1.10"), Err(HttpError::InvalidVersion));
2387 assert_eq!(parse_version(b"HTTP/1"), Err(HttpError::InvalidVersion));
2388 }
2389
2390 #[test]
2391 fn test_target_forms_follow_method() {
2392 assert!(check_target(Method::Get, b"/").is_ok());
2393 assert!(check_target(Method::Options, b"*").is_ok());
2394 assert!(check_target(Method::Get, b"*").is_err());
2395 assert!(check_target(Method::Connect, b"example.com:443").is_ok());
2396 assert!(check_target(Method::Connect, b"[::1]:443").is_ok());
2397 assert!(check_target(Method::Connect, b"example.com").is_err());
2398 assert!(check_target(Method::Connect, b"/").is_err());
2399 assert!(check_target(Method::Get, b"http://example.com/a?b").is_ok());
2400 assert!(check_target(Method::Get, b"http://[::1]:80/").is_ok());
2401 assert!(check_target(Method::Get, b"http://x").is_ok());
2402 assert!(check_target(Method::Get, b"http://x?q").is_ok());
2403 assert!(check_target(Method::Get, b"http://u@x/").is_err());
2404 assert!(check_target(Method::Get, b"http:x").is_err());
2405 assert!(check_target(Method::Get, b"http://:80/").is_err());
2406 assert!(check_target(Method::Get, b"http://x/[a]").is_err());
2407 assert!(check_target(Method::Connect, b":443").is_err());
2408 assert!(check_target(Method::Get, b"1http://x").is_err());
2409 assert!(check_target(Method::Get, b"example.com").is_err());
2410 assert!(check_target(Method::Get, b"").is_err());
2411 }
2412}