Skip to main content

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},
19    syntax::{
20        check_field_value, find_byte, is_host_value, is_ows, is_reason, is_target_chars, is_tchar,
21        is_token, list_elements, parse_decimal, trim_ows,
22    },
23};
24
25/// An offset range into a message head.
26#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
27pub(crate) struct Span {
28    start: u32,
29    end: u32,
30}
31
32impl Span {
33    #[inline]
34    fn new(start: usize, end: usize) -> Self {
35        // Offsets are bounded by the buffer capacity, which `Limits` keeps
36        // far below `u32::MAX`.
37        Self {
38            start: start as u32,
39            end: end as u32,
40        }
41    }
42
43    #[inline]
44    pub(crate) fn get<'a>(&self, buf: &'a [u8]) -> &'a [u8] {
45        buf.get(self.start as usize..self.end as usize)
46            .unwrap_or(&[])
47    }
48}
49
50/// One field line: name and (OWS-trimmed) value.
51#[derive(Clone, Copy, Debug)]
52pub(crate) struct Field {
53    name: Span,
54    value: Span,
55}
56
57#[derive(Clone, Copy, Debug, PartialEq, Eq)]
58pub(crate) enum HeadKind {
59    Request,
60    Response,
61    Trailer,
62}
63
64pub(crate) enum Progress {
65    Partial,
66    /// The head is complete; the value is its length including the blank line.
67    Done(usize),
68}
69
70/// Finds the next CRLF-terminated line starting at `line_start`, resuming
71/// the search at `*scan`. Returns the end of the line content (the index of
72/// its CR). `budget` bounds the line length including CRLF.
73pub(crate) fn find_line(
74    msg: &[u8],
75    line_start: usize,
76    scan: &mut usize,
77    budget: usize,
78    too_long: HttpError,
79) -> Result<Option<usize>, HttpError> {
80    let from = (*scan).max(line_start);
81    let hay = msg.get(from..).unwrap_or(&[]);
82    match find_byte(b'\n', hay) {
83        Some(i) => {
84            let lf = from + i;
85            if lf + 1 - line_start > budget {
86                return Err(too_long);
87            }
88            if lf == line_start || msg.get(lf - 1) != Some(&b'\r') {
89                return Err(HttpError::BareLf);
90            }
91            *scan = lf + 1;
92            Ok(Some(lf - 1))
93        }
94        None => {
95            *scan = msg.len();
96            if msg.len().saturating_sub(line_start) >= budget {
97                Err(too_long)
98            } else {
99                Ok(None)
100            }
101        }
102    }
103}
104
105/// Resumable scanner over a head (start line plus field lines) or a trailer
106/// section (field lines only).
107pub(crate) struct HeadScanner {
108    scan: usize,
109    line_start: usize,
110    start_line: Option<Span>,
111    skipped: usize,
112    field_bytes: usize,
113    count: usize,
114    pub(crate) fields: Vec<Field>,
115}
116
117impl HeadScanner {
118    pub(crate) const fn new() -> Self {
119        Self {
120            scan: 0,
121            line_start: 0,
122            start_line: None,
123            skipped: 0,
124            field_bytes: 0,
125            count: 0,
126            fields: Vec::new(),
127        }
128    }
129
130    /// Prepares for the next message; keeps the field vector's allocation.
131    pub(crate) fn reset(&mut self) {
132        self.scan = 0;
133        self.line_start = 0;
134        self.start_line = None;
135        self.skipped = 0;
136        self.field_bytes = 0;
137        self.count = 0;
138        self.fields.clear();
139    }
140
141    /// Resets only the line state, for a trailer section, keeping the head's
142    /// stored fields readable.
143    pub(crate) fn reset_lines(&mut self) {
144        self.scan = 0;
145        self.line_start = 0;
146        self.skipped = 0;
147        self.field_bytes = 0;
148        self.count = 0;
149    }
150
151    pub(crate) fn start_line(&self) -> Span {
152        self.start_line.unwrap_or_default()
153    }
154
155    /// Scans `msg` (the bytes from the message start to the end of input).
156    pub(crate) fn advance(
157        &mut self,
158        msg: &[u8],
159        limits: &Limits,
160        kind: HeadKind,
161        store: bool,
162    ) -> Result<Progress, HttpError> {
163        loop {
164            let in_start = kind != HeadKind::Trailer && self.start_line.is_none();
165            let (budget, too_long) = if in_start {
166                (
167                    limits.max_start_line().saturating_sub(self.skipped),
168                    HttpError::StartLineTooLong,
169                )
170            } else {
171                (
172                    limits.max_header_bytes().saturating_sub(self.field_bytes),
173                    HttpError::HeadersTooLarge,
174                )
175            };
176            let Some(end) = find_line(msg, self.line_start, &mut self.scan, budget, too_long)?
177            else {
178                return Ok(Progress::Partial);
179            };
180            let start = self.line_start;
181            let line = msg.get(start..end).unwrap_or(&[]);
182            let line_len = self.scan - start;
183            self.line_start = self.scan;
184
185            if in_start {
186                if line.is_empty() {
187                    if kind == HeadKind::Request {
188                        // RFC 9112 section 2.2: ignore empty lines before the
189                        // request line. They count against its budget.
190                        self.skipped += line_len;
191                        continue;
192                    }
193                    return Err(HttpError::InvalidStartLine);
194                }
195                if find_byte(b'\r', line).is_some() {
196                    return Err(HttpError::BareCr);
197                }
198                self.start_line = Some(Span::new(start, end));
199                continue;
200            }
201            if line.is_empty() {
202                return Ok(Progress::Done(self.scan));
203            }
204            self.field_bytes += line_len;
205            // Every field line must leave room for the blank line that ends
206            // the section, so the section as a whole fits the budget.
207            if self.field_bytes + 2 > limits.max_header_bytes() {
208                return Err(HttpError::HeadersTooLarge);
209            }
210            if self.count >= limits.max_headers() {
211                return Err(HttpError::TooManyHeaders);
212            }
213            let field = parse_field(line, start)?;
214            self.count += 1;
215            if store {
216                self.fields.push(field);
217            }
218        }
219    }
220}
221
222/// Validates one field line (`line` starts at offset `base` in the head).
223fn parse_field(line: &[u8], base: usize) -> Result<Field, HttpError> {
224    if line.first().is_some_and(|&b| is_ows(b)) {
225        return Err(HttpError::ObsFold);
226    }
227    let Some(colon) = find_byte(b':', line) else {
228        return Err(if find_byte(b'\r', line).is_some() {
229            HttpError::BareCr
230        } else {
231            HttpError::InvalidHeaderName
232        });
233    };
234    let name = &line[..colon];
235    if name.last().is_some_and(|&b| is_ows(b)) {
236        return Err(HttpError::WhitespaceBeforeColon);
237    }
238    if !name.iter().all(|&b| is_tchar(b)) || name.is_empty() {
239        return Err(if find_byte(b'\r', name).is_some() {
240            HttpError::BareCr
241        } else {
242            HttpError::InvalidHeaderName
243        });
244    }
245    let raw = &line[colon + 1..];
246    let lead = raw.iter().take_while(|&&b| is_ows(b)).count();
247    let value = trim_ows(raw);
248    check_field_value(value)?;
249    let value_start = base + colon + 1 + lead;
250    Ok(Field {
251        name: Span::new(base, base + colon),
252        value: Span::new(value_start, value_start + value.len()),
253    })
254}
255
256/// The HTTP version of a message.
257///
258/// # Examples
259///
260/// ```
261/// use http_net::Version;
262///
263/// assert_eq!(Version::Http11.as_str(), "HTTP/1.1");
264/// ```
265#[non_exhaustive]
266#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
267pub enum Version {
268    /// HTTP/1.0: no chunked encoding, keep-alive only when asked for.
269    Http10,
270    /// HTTP/1.1 (also used for HTTP/1.2 through 1.9, which RFC 9110 section
271    /// 2.5 says to process as the highest implemented minor version).
272    Http11,
273}
274
275impl Version {
276    /// The version's wire text.
277    ///
278    /// # Examples
279    ///
280    /// ```
281    /// assert_eq!(http_net::Version::Http10.as_str(), "HTTP/1.0");
282    /// ```
283    #[must_use]
284    pub const fn as_str(&self) -> &'static str {
285        match self {
286            Self::Http10 => "HTTP/1.0",
287            Self::Http11 => "HTTP/1.1",
288        }
289    }
290}
291
292/// How a message's body is delimited.
293///
294/// # Examples
295///
296/// ```
297/// use http_net::{BodyFraming, ServerConn, ServerEvent};
298///
299/// let mut conn = ServerConn::new();
300/// let _ = conn.feed(b"POST /x HTTP/1.1\r\nHost: a\r\nContent-Length: 3\r\n\r\nabc");
301/// match conn.poll().unwrap() {
302///     ServerEvent::Request(req) => assert_eq!(req.framing(), BodyFraming::Length(3)),
303///     other => panic!("{other:?}"),
304/// }
305/// ```
306#[non_exhaustive]
307#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
308pub enum BodyFraming {
309    /// No body.
310    None,
311    /// Exactly this many bytes (`Content-Length`).
312    Length(u64),
313    /// `Transfer-Encoding: chunked`.
314    Chunked,
315    /// The body runs until the peer closes the connection (responses only).
316    UntilClose,
317}
318
319/// The form of a request target (RFC 9112 section 3.2).
320///
321/// # Examples
322///
323/// ```
324/// use http_net::{ServerConn, ServerEvent, TargetForm};
325///
326/// let mut conn = ServerConn::new();
327/// let _ = conn.feed(b"OPTIONS * HTTP/1.1\r\nHost: a\r\n\r\n");
328/// let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
329/// assert_eq!(req.target_form(), TargetForm::Asterisk);
330/// ```
331#[non_exhaustive]
332#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
333pub enum TargetForm {
334    /// `/path?query`: the usual form for requests to an origin server.
335    Origin,
336    /// `scheme://authority/path?query`: requests to a proxy (and, per RFC
337    /// 9112 section 3.2.2, acceptable to origin servers too).
338    Absolute,
339    /// `host:port`: only with `CONNECT`.
340    Authority,
341    /// `*`: only with `OPTIONS`, for the server as a whole.
342    Asterisk,
343}
344
345/// The `h2c` upgrade token, which is never treated as an upgrade offer
346/// (RFC 9113 section 3.1 deprecates it; forwarded blindly by a proxy, it is
347/// the h2c smuggling attack).
348pub(crate) fn is_h2c(protocol: &[u8]) -> bool {
349    let name = protocol
350        .iter()
351        .position(|&b| b == b'/')
352        .map_or(protocol, |i| &protocol[..i]);
353    name.eq_ignore_ascii_case(b"h2c")
354}
355
356/// Whether a `101` naming `chosen` answers an `Upgrade` offer `element`:
357/// ASCII case-insensitive, and a name without a version matches an offer of
358/// that name with any version (`websocket` answers `websocket/13`).
359pub(crate) fn protocol_matches(chosen: &[u8], element: &[u8]) -> bool {
360    if is_h2c(element) || is_h2c(chosen) {
361        return false;
362    }
363    if chosen.eq_ignore_ascii_case(element) {
364        return true;
365    }
366    !chosen.contains(&b'/')
367        && element
368            .iter()
369            .position(|&b| b == b'/')
370            .is_some_and(|i| element[..i].eq_ignore_ascii_case(chosen))
371}
372
373fn parse_version(v: &[u8]) -> Result<Version, HttpError> {
374    match v {
375        [b'H', b'T', b'T', b'P', b'/', major, b'.', minor]
376            if major.is_ascii_digit() && minor.is_ascii_digit() =>
377        {
378            match (major, minor) {
379                (b'1', b'0') => Ok(Version::Http10),
380                (b'1', _) => Ok(Version::Http11),
381                _ => Err(HttpError::UnsupportedVersion),
382            }
383        }
384        _ => Err(HttpError::InvalidVersion),
385    }
386}
387
388/// What the framing-relevant fields of a message say.
389#[derive(Default)]
390struct FieldFacts {
391    content_length: Option<u64>,
392    te_seen: bool,
393    te_codings: usize,
394    te_chunked: usize,
395    te_last_chunked: bool,
396    te_other: bool,
397    close: bool,
398    keep_alive: bool,
399    upgrade_token: bool,
400    upgrade_field: bool,
401    /// An `Upgrade` element other than `h2c`.
402    upgrade_offer: bool,
403    expect_continue: bool,
404    hosts: usize,
405    host: Option<Span>,
406}
407
408fn scan_fields(head: &[u8], fields: &[Field]) -> Result<FieldFacts, HttpError> {
409    let mut facts = FieldFacts::default();
410    for field in fields {
411        let name = field.name.get(head);
412        let value = field.value.get(head);
413        if name.eq_ignore_ascii_case(b"content-length") {
414            if facts.content_length.is_some() {
415                return Err(HttpError::DuplicateContentLength);
416            }
417            facts.content_length =
418                Some(parse_decimal(value).ok_or(HttpError::InvalidContentLength)?);
419        } else if name.eq_ignore_ascii_case(b"transfer-encoding") {
420            facts.te_seen = true;
421            for element in list_elements(value) {
422                let (coding, params) = match find_byte(b';', element) {
423                    Some(i) => (trim_ows(&element[..i]), true),
424                    None => (element, false),
425                };
426                if !is_token(coding) {
427                    return Err(HttpError::InvalidTransferEncoding);
428                }
429                facts.te_codings += 1;
430                if coding.eq_ignore_ascii_case(b"chunked") {
431                    if params {
432                        return Err(HttpError::InvalidTransferEncoding);
433                    }
434                    facts.te_chunked += 1;
435                    facts.te_last_chunked = true;
436                } else {
437                    facts.te_other = true;
438                    facts.te_last_chunked = false;
439                }
440            }
441        } else if name.eq_ignore_ascii_case(b"connection") {
442            for token in list_elements(value) {
443                if token.eq_ignore_ascii_case(b"close") {
444                    facts.close = true;
445                } else if token.eq_ignore_ascii_case(b"keep-alive") {
446                    facts.keep_alive = true;
447                } else if token.eq_ignore_ascii_case(b"upgrade") {
448                    facts.upgrade_token = true;
449                }
450            }
451        } else if name.eq_ignore_ascii_case(b"upgrade") {
452            facts.upgrade_field |= !value.is_empty();
453            facts.upgrade_offer |= list_elements(value).any(|e| !is_h2c(e));
454        } else if name.eq_ignore_ascii_case(b"host") {
455            facts.hosts += 1;
456            facts.host = Some(field.value);
457        } else if name.eq_ignore_ascii_case(b"expect") {
458            // RFC 9110 section 10.1.1: the expectation is case-insensitive.
459            facts.expect_continue |=
460                list_elements(value).any(|e| e.eq_ignore_ascii_case(b"100-continue"));
461        }
462    }
463    Ok(facts)
464}
465
466/// Applies RFC 9112 section 6 to the transfer-coding facts.
467fn transfer_framing(
468    facts: &FieldFacts,
469    version: Version,
470) -> Result<Option<BodyFraming>, HttpError> {
471    if !facts.te_seen {
472        return Ok(None);
473    }
474    if version == Version::Http10 {
475        return Err(HttpError::TransferEncodingInHttp10);
476    }
477    if facts.content_length.is_some() {
478        return Err(HttpError::ConflictingFraming);
479    }
480    if facts.te_codings == 0 || facts.te_chunked > 1 || !facts.te_last_chunked {
481        return Err(HttpError::InvalidTransferEncoding);
482    }
483    if facts.te_other {
484        return Err(HttpError::UnsupportedTransferEncoding);
485    }
486    Ok(Some(BodyFraming::Chunked))
487}
488
489/// Everything learned from a request head.
490#[derive(Clone, Copy, Debug)]
491pub(crate) struct RequestInfo {
492    pub(crate) method: Method,
493    method_span: Span,
494    target: Span,
495    target_form: TargetForm,
496    pub(crate) version: Version,
497    pub(crate) framing: BodyFraming,
498    content_length: Option<u64>,
499    pub(crate) keep_alive: bool,
500    pub(crate) upgrade: bool,
501    pub(crate) expect_continue: bool,
502    host: Option<Span>,
503    /// The authority of an absolute-form target, which replaces `Host`.
504    authority: Option<Span>,
505    /// The path-and-query part of the target: the whole target except for
506    /// the absolute form, where it is what follows the authority.
507    origin: Span,
508}
509
510impl RequestInfo {
511    pub(crate) const EMPTY: Self = Self {
512        method: Method::Get,
513        method_span: Span { start: 0, end: 0 },
514        target: Span { start: 0, end: 0 },
515        target_form: TargetForm::Origin,
516        version: Version::Http11,
517        framing: BodyFraming::None,
518        content_length: None,
519        keep_alive: false,
520        upgrade: false,
521        expect_continue: false,
522        host: None,
523        authority: None,
524        origin: Span { start: 0, end: 0 },
525    };
526}
527
528/// Validates a request target against the forms its method allows.
529pub(crate) fn check_target(method: Method, target: &[u8]) -> Result<(), HttpError> {
530    let ok = match target.first() {
531        None => false,
532        Some(b'/') => method != Method::Connect && is_target_chars(target),
533        Some(b'*') if target.len() == 1 => method == Method::Options,
534        Some(_) if method == Method::Connect => {
535            // authority-form: host ":" port, with a non-empty host and port.
536            is_authority(target)
537                && target
538                    .iter()
539                    .rposition(|&b| b == b':')
540                    .is_some_and(|c| c + 1 < target.len() && !target[c..].contains(&b']'))
541        }
542        Some(_) => split_absolute_form(target).is_some(),
543    };
544    if ok {
545        Ok(())
546    } else {
547        Err(HttpError::InvalidTarget)
548    }
549}
550
551/// `host [":" port]` with a non-empty host: RFC 9110 section 4.2.1 makes an
552/// empty host in an `http` URI invalid.
553fn is_authority(s: &[u8]) -> bool {
554    !s.is_empty() && s[0] != b':' && is_host_value(s)
555}
556
557/// Splits an absolute-form target, `scheme "://" authority path-abempty
558/// [ "?" query ]`, returning the authority's range within it.
559///
560/// Only the hierarchical form with an authority is accepted: it is the form
561/// RFC 9112 section 3.2.2 describes (requests to proxies, and future
562/// requests to origin servers), and it is the only one that names the host
563/// the request is for. Userinfo is refused (RFC 9110 section 4.2.4 says to
564/// treat it as an error), as is an empty host.
565fn split_absolute_form(target: &[u8]) -> Option<(usize, usize)> {
566    let colon = find_byte(b':', target)?;
567    let scheme = &target[..colon];
568    let scheme_ok = scheme.first().is_some_and(u8::is_ascii_alphabetic)
569        && scheme
570            .iter()
571            .all(|&b| b.is_ascii_alphanumeric() || matches!(b, b'+' | b'-' | b'.'));
572    if !scheme_ok || !target[colon..].starts_with(b"://") {
573        return None;
574    }
575    let auth_start = colon + 3;
576    let after = &target[auth_start..];
577    let auth_len = after
578        .iter()
579        .position(|&b| b == b'/' || b == b'?')
580        .unwrap_or(after.len());
581    let authority = &after[..auth_len];
582    let rest = &after[auth_len..];
583    (is_authority(authority) && is_target_chars(rest))
584        .then_some((auth_start, auth_start + auth_len))
585}
586
587pub(crate) fn interpret_request(
588    head: &[u8],
589    start: Span,
590    fields: &[Field],
591    limits: &Limits,
592) -> Result<RequestInfo, HttpError> {
593    let line = start.get(head);
594    let base = start.start as usize;
595    let sp1 = find_byte(b' ', line).ok_or(HttpError::InvalidStartLine)?;
596    let sp2 = line
597        .iter()
598        .rposition(|&b| b == b' ')
599        .ok_or(HttpError::InvalidStartLine)?;
600    if sp2 == sp1 {
601        return Err(HttpError::InvalidStartLine);
602    }
603    let method_bytes = &line[..sp1];
604    if !is_token(method_bytes) {
605        return Err(HttpError::InvalidMethod);
606    }
607    let version = parse_version(&line[sp2 + 1..])?;
608    let method = Method::from_bytes(method_bytes);
609    let target = &line[sp1 + 1..sp2];
610    check_target(method, target)?;
611    let target_start = base + sp1 + 1;
612    let target_span = Span::new(target_start, base + sp2);
613    let (target_form, authority, origin) = match target.first() {
614        Some(b'/') => (TargetForm::Origin, None, target_span),
615        Some(b'*') => (TargetForm::Asterisk, None, target_span),
616        _ if method == Method::Connect => (TargetForm::Authority, None, target_span),
617        _ => match split_absolute_form(target) {
618            Some((a, b)) => (
619                TargetForm::Absolute,
620                Some(Span::new(target_start + a, target_start + b)),
621                Span::new(target_start + b, base + sp2),
622            ),
623            None => return Err(HttpError::InvalidTarget),
624        },
625    };
626
627    let facts = scan_fields(head, fields)?;
628    let framing = match transfer_framing(&facts, version)? {
629        Some(f) => f,
630        None => match facts.content_length {
631            Some(n) if n > limits.max_body() => return Err(HttpError::BodyTooLarge),
632            Some(0) | None => BodyFraming::None,
633            Some(n) => BodyFraming::Length(n),
634        },
635    };
636    if facts.hosts > 1 {
637        return Err(HttpError::InvalidHost);
638    }
639    if let Some(host) = facts.host {
640        if !is_host_value(host.get(head)) {
641            return Err(HttpError::InvalidHost);
642        }
643    } else if version == Version::Http11 {
644        return Err(HttpError::MissingHost);
645    }
646    let keep_alive = match version {
647        Version::Http11 => !facts.close,
648        Version::Http10 => facts.keep_alive && !facts.close,
649    };
650    Ok(RequestInfo {
651        method,
652        method_span: Span::new(base, base + sp1),
653        target: target_span,
654        target_form,
655        version,
656        framing,
657        content_length: facts.content_length,
658        keep_alive,
659        // RFC 9110 section 7.8: Upgrade in an HTTP/1.0 request is ignored.
660        // An offer of only `h2c` is no offer: the request is served as
661        // HTTP/1.1 (RFC 9113 section 3.1).
662        upgrade: version == Version::Http11 && facts.upgrade_token && facts.upgrade_offer,
663        // RFC 9110 section 10.1.1: ignored in HTTP/1.0, and meaningless
664        // without a body to wait for.
665        expect_continue: version == Version::Http11
666            && facts.expect_continue
667            && framing != BodyFraming::None,
668        host: facts.host,
669        authority,
670        origin,
671    })
672}
673
674/// Everything learned from a response head.
675#[derive(Clone, Copy, Debug)]
676pub(crate) struct ResponseInfo {
677    pub(crate) status: u16,
678    reason: Span,
679    pub(crate) version: Version,
680    pub(crate) framing: BodyFraming,
681    content_length: Option<u64>,
682    pub(crate) keep_alive: bool,
683    pub(crate) upgrade_field: bool,
684}
685
686impl ResponseInfo {
687    pub(crate) const EMPTY: Self = Self {
688        status: 0,
689        reason: Span { start: 0, end: 0 },
690        version: Version::Http11,
691        framing: BodyFraming::None,
692        content_length: None,
693        keep_alive: false,
694        upgrade_field: false,
695    };
696}
697
698/// What the client asked for, which decides how the response is framed.
699#[derive(Clone, Copy, Debug)]
700pub(crate) struct Asked {
701    pub(crate) head: bool,
702    pub(crate) connect: bool,
703}
704
705pub(crate) fn interpret_response(
706    head: &[u8],
707    start: Span,
708    fields: &[Field],
709    limits: &Limits,
710    asked: Asked,
711) -> Result<ResponseInfo, HttpError> {
712    let line = start.get(head);
713    let base = start.start as usize;
714    // status-line = HTTP-version SP status-code SP [ reason-phrase ]
715    // The SP after the code is required of senders, but some servers omit
716    // it when the reason is empty (`HTTP/1.1 200`); accepting that cannot
717    // change framing, so the client does (RFC 9112 section 4).
718    if line.len() < 12 || line[8] != b' ' || (line.len() > 12 && line[12] != b' ') {
719        return Err(HttpError::InvalidStartLine);
720    }
721    let version = parse_version(&line[..8])?;
722    let code = &line[9..12];
723    if !code.iter().all(u8::is_ascii_digit) {
724        return Err(HttpError::InvalidStatus);
725    }
726    let status = code
727        .iter()
728        .fold(0u16, |acc, &d| acc * 10 + u16::from(d - b'0'));
729    if !(100..=599).contains(&status) {
730        return Err(HttpError::InvalidStatus);
731    }
732    let reason_start = line.len().min(13);
733    let reason = &line[reason_start..];
734    if !is_reason(reason) {
735        return Err(HttpError::InvalidStartLine);
736    }
737
738    let facts = scan_fields(head, fields)?;
739    let bodiless = asked.head
740        || (100..200).contains(&status)
741        || status == 204
742        || status == 304
743        || (asked.connect && (200..300).contains(&status));
744    let framing = if bodiless {
745        BodyFraming::None
746    } else {
747        match transfer_framing(&facts, version)? {
748            Some(f) => f,
749            None => match facts.content_length {
750                Some(n) if n > limits.max_body() => return Err(HttpError::BodyTooLarge),
751                Some(0) => BodyFraming::None,
752                Some(n) => BodyFraming::Length(n),
753                None => BodyFraming::UntilClose,
754            },
755        }
756    };
757    let keep_alive = framing != BodyFraming::UntilClose
758        && match version {
759            Version::Http11 => !facts.close,
760            Version::Http10 => facts.keep_alive && !facts.close,
761        };
762    Ok(ResponseInfo {
763        status,
764        reason: Span::new(base + reason_start, base + line.len()),
765        version,
766        framing,
767        content_length: facts.content_length,
768        keep_alive,
769        upgrade_field: facts.upgrade_field,
770    })
771}
772
773/// Iterator over a head's fields as `(name, value)` pairs, in arrival order.
774///
775/// Names are returned as received (field names are case-insensitive; compare
776/// with `eq_ignore_ascii_case`). Values have surrounding whitespace removed
777/// and may contain any byte RFC 9110 allows, including `obs-text` above
778/// 0x7F, so they are bytes rather than `str`.
779///
780/// # Examples
781///
782/// ```
783/// use http_net::{ServerConn, ServerEvent};
784///
785/// let mut conn = ServerConn::new();
786/// let _ = conn.feed(b"GET / HTTP/1.1\r\nHost: a\r\nX-One: 1\r\n\r\n");
787/// if let ServerEvent::Request(req) = conn.poll().unwrap() {
788///     let names: Vec<&str> = req.headers().map(|(n, _)| n).collect();
789///     assert_eq!(names, ["Host", "X-One"]);
790/// }
791/// ```
792#[derive(Clone)]
793pub struct Headers<'a> {
794    head: &'a [u8],
795    fields: slice::Iter<'a, Field>,
796}
797
798impl<'a> Iterator for Headers<'a> {
799    type Item = (&'a str, &'a [u8]);
800
801    #[inline]
802    fn next(&mut self) -> Option<Self::Item> {
803        let field = self.fields.next()?;
804        Some((
805            core::str::from_utf8(field.name.get(self.head)).unwrap_or(""),
806            field.value.get(self.head),
807        ))
808    }
809
810    #[inline]
811    fn size_hint(&self) -> (usize, Option<usize>) {
812        self.fields.size_hint()
813    }
814}
815
816impl ExactSizeIterator for Headers<'_> {}
817
818/// Shows the fields as `(name, value)` pairs; the values of credential
819/// fields (`authorization`, `proxy-authorization`, `cookie`, `set-cookie`)
820/// are replaced by `<redacted N bytes>`.
821impl fmt::Debug for Headers<'_> {
822    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
823        let head = self.head;
824        f.debug_list()
825            .entries(self.fields.clone().map(|field| {
826                let name = field.name.get(head);
827                (
828                    FieldName(name),
829                    FieldValue {
830                        name,
831                        value: field.value.get(head),
832                    },
833                )
834            }))
835            .finish()
836    }
837}
838
839/// Iterator over the values of every field with one name, in arrival
840/// order: what [`RequestHead::header_all`] and
841/// [`ResponseHead::header_all`] return. Each `Set-Cookie`, for example, is
842/// its own field and its own item; nothing is comma-joined.
843///
844/// # Examples
845///
846/// ```
847/// use http_net::{ServerConn, ServerEvent};
848///
849/// let mut conn = ServerConn::new();
850/// 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");
851/// let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
852/// let vias: Vec<&[u8]> = req.header_all("via").collect();
853/// assert_eq!(vias, [&b"1.1 p1"[..], &b"1.1 p2"[..]]);
854/// ```
855///
856/// The values borrow the head (`'a`), not the name (`'n`), so they outlive
857/// a temporary name used for the lookup.
858#[derive(Clone)]
859pub struct HeaderValues<'a, 'n> {
860    head: &'a [u8],
861    fields: slice::Iter<'a, Field>,
862    name: &'n [u8],
863}
864
865impl<'a> Iterator for HeaderValues<'a, '_> {
866    type Item = &'a [u8];
867
868    #[inline]
869    fn next(&mut self) -> Option<Self::Item> {
870        let (head, name) = (self.head, self.name);
871        self.fields
872            .find(|f| f.name.get(head).eq_ignore_ascii_case(name))
873            .map(|f| f.value.get(head))
874    }
875
876    #[inline]
877    fn size_hint(&self) -> (usize, Option<usize>) {
878        (0, self.fields.size_hint().1)
879    }
880}
881
882impl fmt::Debug for HeaderValues<'_, '_> {
883    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
884        let name = self.name;
885        f.debug_list()
886            .entries(self.clone().map(|value| FieldValue { name, value }))
887            .finish()
888    }
889}
890
891fn find_header<'a>(head: &'a [u8], fields: &[Field], name: &str) -> Option<&'a [u8]> {
892    fields
893        .iter()
894        .find(|f| f.name.get(head).eq_ignore_ascii_case(name.as_bytes()))
895        .map(|f| f.value.get(head))
896}
897
898fn header_values<'a, 'n>(
899    head: &'a [u8],
900    fields: &'a [Field],
901    name: &'n str,
902) -> HeaderValues<'a, 'n> {
903    HeaderValues {
904        head,
905        fields: fields.iter(),
906        name: name.as_bytes(),
907    }
908}
909
910/// A parsed request head, borrowed from the connection's buffer.
911///
912/// Every accessor is a slice of the bytes that arrived; nothing is copied.
913/// The view stays valid until the next call that mutates the connection.
914/// Read it again later with [`ServerConn::request`](crate::ServerConn::request).
915///
916/// # Examples
917///
918/// ```
919/// use http_net::{Method, ServerConn, ServerEvent, Version};
920///
921/// let mut conn = ServerConn::new();
922/// let _ = conn.feed(b"GET /search?q=rust HTTP/1.1\r\nHost: example.com\r\n\r\n");
923/// let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
924/// assert_eq!(req.method(), Method::Get);
925/// assert_eq!(req.path(), "/search");
926/// assert_eq!(req.query(), Some("q=rust"));
927/// assert_eq!(req.version(), Version::Http11);
928/// assert_eq!(req.host(), Some("example.com"));
929/// assert!(req.keep_alive());
930/// ```
931#[derive(Clone, Copy)]
932pub struct RequestHead<'a> {
933    head: &'a [u8],
934    fields: &'a [Field],
935    info: &'a RequestInfo,
936}
937
938impl<'a> RequestHead<'a> {
939    pub(crate) const fn new(head: &'a [u8], fields: &'a [Field], info: &'a RequestInfo) -> Self {
940        Self { head, fields, info }
941    }
942
943    /// The method, classified.
944    ///
945    /// # Examples
946    ///
947    /// ```
948    /// # use http_net::{Method, ServerConn, ServerEvent};
949    /// # let mut conn = ServerConn::new();
950    /// # let _ = conn.feed(b"DELETE /x HTTP/1.1\r\nHost: a\r\n\r\n");
951    /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
952    /// assert_eq!(req.method(), Method::Delete);
953    /// ```
954    #[must_use]
955    pub fn method(&self) -> Method {
956        self.info.method
957    }
958
959    /// The method exactly as received.
960    ///
961    /// # Examples
962    ///
963    /// ```
964    /// # use http_net::{Method, ServerConn, ServerEvent};
965    /// # let mut conn = ServerConn::new();
966    /// # let _ = conn.feed(b"PROPFIND /x HTTP/1.1\r\nHost: a\r\n\r\n");
967    /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
968    /// assert_eq!(req.method(), Method::Other);
969    /// assert_eq!(req.method_str(), "PROPFIND");
970    /// ```
971    #[must_use]
972    pub fn method_str(&self) -> &'a str {
973        core::str::from_utf8(self.info.method_span.get(self.head)).unwrap_or("")
974    }
975
976    /// The request target exactly as received (origin form `/path?query`,
977    /// absolute form, authority form for `CONNECT`, or `*`).
978    ///
979    /// # Examples
980    ///
981    /// ```
982    /// # use http_net::{ServerConn, ServerEvent};
983    /// # let mut conn = ServerConn::new();
984    /// # let _ = conn.feed(b"GET /a%20b?x=1 HTTP/1.1\r\nHost: a\r\n\r\n");
985    /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
986    /// assert_eq!(req.target(), "/a%20b?x=1");
987    /// ```
988    #[must_use]
989    pub fn target(&self) -> &'a str {
990        core::str::from_utf8(self.info.target.get(self.head)).unwrap_or("")
991    }
992
993    /// Which of the four RFC 9112 section 3.2 forms the target uses.
994    ///
995    /// # Examples
996    ///
997    /// ```
998    /// # use http_net::{ServerConn, ServerEvent, TargetForm};
999    /// # let mut conn = ServerConn::new();
1000    /// # let _ = conn.feed(b"GET http://a.test/x HTTP/1.1\r\nHost: a.test\r\n\r\n");
1001    /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1002    /// assert_eq!(req.target_form(), TargetForm::Absolute);
1003    /// ```
1004    #[must_use]
1005    pub fn target_form(&self) -> TargetForm {
1006        self.info.target_form
1007    }
1008
1009    /// The path: the target up to (not including) the first `?`, with
1010    /// percent-encoding left as received.
1011    ///
1012    /// For an absolute-form target (`http://host/p?q`) this is the path
1013    /// after the authority (`/p`), and `/` when that path is empty, so a
1014    /// router sees the same path whichever form the client used. For `*`
1015    /// and for `CONNECT`'s authority form it is the whole target.
1016    ///
1017    /// # Examples
1018    ///
1019    /// ```
1020    /// # use http_net::{ServerConn, ServerEvent};
1021    /// # let mut conn = ServerConn::new();
1022    /// # let _ = conn.feed(b"GET /files/a.txt?v=2 HTTP/1.1\r\nHost: a\r\n\r\n");
1023    /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1024    /// assert_eq!(req.path(), "/files/a.txt");
1025    ///
1026    /// # let mut conn = ServerConn::new();
1027    /// # let _ = conn.feed(b"GET http://a/files/b.txt HTTP/1.1\r\nHost: a\r\n\r\n");
1028    /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1029    /// assert_eq!(req.path(), "/files/b.txt");
1030    /// ```
1031    #[must_use]
1032    pub fn path(&self) -> &'a str {
1033        let origin = self.origin();
1034        let path = origin.split_once('?').map_or(origin, |(p, _)| p);
1035        if path.is_empty() && self.info.authority.is_some() {
1036            // RFC 9110 section 4.2.3: an empty path in an http URI is "/".
1037            "/"
1038        } else {
1039            path
1040        }
1041    }
1042
1043    /// The query after the first `?`, if there is one.
1044    ///
1045    /// # Examples
1046    ///
1047    /// ```
1048    /// # use http_net::{ServerConn, ServerEvent};
1049    /// # let mut conn = ServerConn::new();
1050    /// # let _ = conn.feed(b"GET /x HTTP/1.1\r\nHost: a\r\n\r\n");
1051    /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1052    /// assert_eq!(req.query(), None);
1053    /// ```
1054    #[must_use]
1055    pub fn query(&self) -> Option<&'a str> {
1056        self.origin().split_once('?').map(|(_, q)| q)
1057    }
1058
1059    /// The path-and-query part of the target.
1060    fn origin(&self) -> &'a str {
1061        core::str::from_utf8(self.info.origin.get(self.head)).unwrap_or("")
1062    }
1063
1064    /// The protocol version.
1065    ///
1066    /// # Examples
1067    ///
1068    /// ```
1069    /// # use http_net::{ServerConn, ServerEvent, Version};
1070    /// # let mut conn = ServerConn::new();
1071    /// # let _ = conn.feed(b"GET /x HTTP/1.0\r\n\r\n");
1072    /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1073    /// assert_eq!(req.version(), Version::Http10);
1074    /// ```
1075    #[must_use]
1076    pub fn version(&self) -> Version {
1077        self.info.version
1078    }
1079
1080    /// All fields in arrival order.
1081    ///
1082    /// # Examples
1083    ///
1084    /// ```
1085    /// # use http_net::{ServerConn, ServerEvent};
1086    /// # let mut conn = ServerConn::new();
1087    /// # let _ = conn.feed(b"GET /x HTTP/1.1\r\nHost: a\r\nAccept: */*\r\n\r\n");
1088    /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1089    /// assert_eq!(req.headers().len(), 2);
1090    /// ```
1091    #[must_use]
1092    pub fn headers(&self) -> Headers<'a> {
1093        Headers {
1094            head: self.head,
1095            fields: self.fields.iter(),
1096        }
1097    }
1098
1099    /// The value of the first field named `name` (ASCII case-insensitive).
1100    ///
1101    /// # Examples
1102    ///
1103    /// ```
1104    /// # use http_net::{ServerConn, ServerEvent};
1105    /// # let mut conn = ServerConn::new();
1106    /// # let _ = conn.feed(b"GET /x HTTP/1.1\r\nHost: a\r\nAccept:  text/html \r\n\r\n");
1107    /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1108    /// assert_eq!(req.header("accept"), Some(&b"text/html"[..]));
1109    /// assert_eq!(req.header("cookie"), None);
1110    /// ```
1111    #[must_use]
1112    pub fn header(&self, name: &str) -> Option<&'a [u8]> {
1113        find_header(self.head, self.fields, name)
1114    }
1115
1116    /// Like [`header`](Self::header), but only when the value is UTF-8.
1117    ///
1118    /// # Examples
1119    ///
1120    /// ```
1121    /// # use http_net::{ServerConn, ServerEvent};
1122    /// # let mut conn = ServerConn::new();
1123    /// # let _ = conn.feed(b"GET /x HTTP/1.1\r\nHost: a\r\nX: \xff\r\n\r\n");
1124    /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1125    /// assert_eq!(req.header_str("host"), Some("a"));
1126    /// assert_eq!(req.header_str("x"), None);
1127    /// ```
1128    #[must_use]
1129    pub fn header_str(&self, name: &str) -> Option<&'a str> {
1130        self.header(name).and_then(|v| core::str::from_utf8(v).ok())
1131    }
1132
1133    /// The values of every field named `name` (ASCII case-insensitive), in
1134    /// arrival order: for fields that may be repeated, such as `via`,
1135    /// `forwarded`, or `cookie` from clients that send one line per cookie.
1136    ///
1137    /// # Examples
1138    ///
1139    /// ```
1140    /// # use http_net::{ServerConn, ServerEvent};
1141    /// # let mut conn = ServerConn::new();
1142    /// # let _ = conn.feed(b"GET /x HTTP/1.1\r\nHost: a\r\nCookie: a=1\r\nCookie: b=2\r\n\r\n");
1143    /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1144    /// let cookies: Vec<&[u8]> = req.header_all("cookie").collect();
1145    /// assert_eq!(cookies, [&b"a=1"[..], &b"b=2"[..]]);
1146    /// assert_eq!(req.header_all("accept").count(), 0);
1147    ///
1148    /// // The values borrow the head, not the name.
1149    /// let first = {
1150    ///     let name = String::from("COOKIE");
1151    ///     req.header_all(&name).next()
1152    /// };
1153    /// assert_eq!(first, Some(&b"a=1"[..]));
1154    /// ```
1155    #[must_use]
1156    pub fn header_all<'n>(&self, name: &'n str) -> HeaderValues<'a, 'n> {
1157        header_values(self.head, self.fields, name)
1158    }
1159
1160    /// The declared `Content-Length`, if the request has one. Unlike
1161    /// [`framing`](Self::framing), which reports `Content-Length: 0` as
1162    /// [`BodyFraming::None`], this tells `Some(0)` from an absent field.
1163    ///
1164    /// # Examples
1165    ///
1166    /// ```
1167    /// # use http_net::{BodyFraming, ServerConn, ServerEvent};
1168    /// # let mut conn = ServerConn::new();
1169    /// # let _ = conn.feed(b"POST /x HTTP/1.1\r\nHost: a\r\nContent-Length: 0\r\n\r\n");
1170    /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1171    /// assert_eq!(req.framing(), BodyFraming::None);
1172    /// assert_eq!(req.content_length(), Some(0));
1173    /// ```
1174    #[must_use]
1175    pub fn content_length(&self) -> Option<u64> {
1176        self.info.content_length
1177    }
1178
1179    /// The authority the request is for (`host [":" port]`), if known.
1180    ///
1181    /// This is the `Host` field, except for an absolute-form target
1182    /// (`GET http://example.com/ HTTP/1.1`), whose authority wins: RFC 9112
1183    /// section 3.2.2 requires a server to ignore `Host` then, so the two can
1184    /// never be read differently. The raw field stays available through
1185    /// `header("host")`. HTTP/1.1 requests always carry a valid `Host`;
1186    /// HTTP/1.0 requests may not, and an empty `Host` (a target with no
1187    /// authority) is reported as `Some("")`.
1188    ///
1189    /// # Examples
1190    ///
1191    /// ```
1192    /// # use http_net::{ServerConn, ServerEvent};
1193    /// # let mut conn = ServerConn::new();
1194    /// # let _ = conn.feed(b"GET /x HTTP/1.1\r\nHost: 127.0.0.1:8080\r\n\r\n");
1195    /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1196    /// assert_eq!(req.host(), Some("127.0.0.1:8080"));
1197    ///
1198    /// # let mut conn = ServerConn::new();
1199    /// # let _ = conn.feed(b"GET http://a.test/x HTTP/1.1\r\nHost: b.test\r\n\r\n");
1200    /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1201    /// assert_eq!(req.host(), Some("a.test"));
1202    /// assert_eq!(req.header("host"), Some(&b"b.test"[..]));
1203    /// ```
1204    #[must_use]
1205    pub fn host(&self) -> Option<&'a str> {
1206        self.info
1207            .authority
1208            .or(self.info.host)
1209            .and_then(|s| core::str::from_utf8(s.get(self.head)).ok())
1210    }
1211
1212    /// Whether the client sent `Expect: 100-continue` and is waiting for a
1213    /// `100 Continue` before sending the body. Only HTTP/1.1 requests that
1214    /// have a body report it (RFC 9110 section 10.1.1).
1215    ///
1216    /// Answer it before reading the body: either
1217    /// [`ServerConn::send_continue`](crate::ServerConn::send_continue), or a
1218    /// final response straight away (which closes the connection afterwards,
1219    /// since the body was not read). The blocking `Server` sends
1220    /// `100 Continue` itself.
1221    ///
1222    /// # Examples
1223    ///
1224    /// ```
1225    /// # use http_net::{ServerConn, ServerEvent};
1226    /// # let mut conn = ServerConn::new();
1227    /// # let _ = conn.feed(b"PUT /f HTTP/1.1\r\nHost: a\r\nExpect: 100-continue\r\nContent-Length: 9\r\n\r\n");
1228    /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1229    /// assert!(req.expects_continue());
1230    /// ```
1231    #[must_use]
1232    pub fn expects_continue(&self) -> bool {
1233        self.info.expect_continue
1234    }
1235
1236    /// How the request body is delimited.
1237    ///
1238    /// # Examples
1239    ///
1240    /// ```
1241    /// # use http_net::{BodyFraming, ServerConn, ServerEvent};
1242    /// # let mut conn = ServerConn::new();
1243    /// # let _ = conn.feed(b"POST /x HTTP/1.1\r\nHost: a\r\nTransfer-Encoding: chunked\r\n\r\n");
1244    /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1245    /// assert_eq!(req.framing(), BodyFraming::Chunked);
1246    /// ```
1247    #[must_use]
1248    pub fn framing(&self) -> BodyFraming {
1249        self.info.framing
1250    }
1251
1252    /// Whether the client allows the connection to stay open after this
1253    /// exchange (HTTP/1.1 unless `Connection: close`; HTTP/1.0 only with
1254    /// `Connection: keep-alive`).
1255    ///
1256    /// # Examples
1257    ///
1258    /// ```
1259    /// # use http_net::{ServerConn, ServerEvent};
1260    /// # let mut conn = ServerConn::new();
1261    /// # let _ = conn.feed(b"GET /x HTTP/1.1\r\nHost: a\r\nConnection: close\r\n\r\n");
1262    /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1263    /// assert!(!req.keep_alive());
1264    /// ```
1265    #[must_use]
1266    pub fn keep_alive(&self) -> bool {
1267        self.info.keep_alive
1268    }
1269
1270    /// Whether this is an HTTP/1.1 request to switch protocols: `Connection`
1271    /// lists `upgrade` and an `Upgrade` field offers at least one protocol
1272    /// other than `h2c` (read the offer with `header("upgrade")`). Answer
1273    /// with [`Response::switching_protocols`](crate::Response::switching_protocols)
1274    /// naming an offered protocol to hand the connection over.
1275    ///
1276    /// `h2c` is never an offer: RFC 9113 section 3.1 deprecates the
1277    /// HTTP/2 upgrade, and a proxy that forwards it blindly is open to h2c
1278    /// smuggling. A request offering only `h2c` is served as HTTP/1.1.
1279    ///
1280    /// # Examples
1281    ///
1282    /// ```
1283    /// # use http_net::{ServerConn, ServerEvent};
1284    /// # let mut conn = ServerConn::new();
1285    /// # let _ = conn.feed(b"GET /ws HTTP/1.1\r\nHost: a\r\nConnection: Upgrade\r\nUpgrade: websocket\r\n\r\n");
1286    /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1287    /// assert!(req.is_upgrade());
1288    /// assert_eq!(req.header("upgrade"), Some(&b"websocket"[..]));
1289    ///
1290    /// # let mut conn = ServerConn::new();
1291    /// # 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");
1292    /// # let ServerEvent::Request(req) = conn.poll().unwrap() else { panic!() };
1293    /// assert!(!req.is_upgrade());
1294    /// ```
1295    #[must_use]
1296    pub fn is_upgrade(&self) -> bool {
1297        self.info.upgrade
1298    }
1299
1300    /// Whether the request's `Upgrade` field offers `protocol` (see
1301    /// [`Response::switching_protocols`](crate::Response::switching_protocols)).
1302    pub(crate) fn offers(&self, protocol: &[u8]) -> bool {
1303        self.info.upgrade
1304            && self
1305                .header_all("upgrade")
1306                .any(|v| list_elements(v).any(|e| protocol_matches(protocol, e)))
1307    }
1308}
1309
1310/// Shows method, target, version, fields, and framing; the values of
1311/// credential fields are replaced by `<redacted N bytes>`.
1312impl fmt::Debug for RequestHead<'_> {
1313    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1314        f.debug_struct("RequestHead")
1315            .field("method", &self.method_str())
1316            .field("target", &self.target())
1317            .field("version", &self.version())
1318            .field("headers", &self.headers())
1319            .field("framing", &self.framing())
1320            .finish()
1321    }
1322}
1323
1324/// A parsed response head, borrowed from the client connection's buffer.
1325///
1326/// # Examples
1327///
1328/// ```
1329/// use http_net::{ClientConn, ClientEvent, Request};
1330///
1331/// let mut conn = ClientConn::new();
1332/// let mut out = Vec::new();
1333/// conn.send_request(&Request::get("/").header("host", "a"), &mut out).unwrap();
1334/// let _ = conn.feed(b"HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n");
1335/// let ClientEvent::Response(resp) = conn.poll().unwrap() else { panic!() };
1336/// assert_eq!(resp.status(), 404);
1337/// assert_eq!(resp.reason(), "Not Found");
1338/// ```
1339#[derive(Clone, Copy)]
1340pub struct ResponseHead<'a> {
1341    head: &'a [u8],
1342    fields: &'a [Field],
1343    info: &'a ResponseInfo,
1344}
1345
1346impl<'a> ResponseHead<'a> {
1347    pub(crate) const fn new(head: &'a [u8], fields: &'a [Field], info: &'a ResponseInfo) -> Self {
1348        Self { head, fields, info }
1349    }
1350
1351    /// The status code (`100..=599`).
1352    ///
1353    /// # Examples
1354    ///
1355    /// ```
1356    /// # use http_net::{ClientConn, ClientEvent, Request};
1357    /// # let mut conn = ClientConn::new();
1358    /// # conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new()).unwrap();
1359    /// # let _ = conn.feed(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
1360    /// # let ClientEvent::Response(resp) = conn.poll().unwrap() else { panic!() };
1361    /// assert_eq!(resp.status(), 200);
1362    /// ```
1363    #[must_use]
1364    pub fn status(&self) -> u16 {
1365        self.info.status
1366    }
1367
1368    /// The reason phrase, if it is UTF-8 (it carries no meaning; RFC 9112
1369    /// tells clients to ignore it). Empty for a status line without one,
1370    /// including `HTTP/1.1 200` with no space after the code.
1371    ///
1372    /// # Examples
1373    ///
1374    /// ```
1375    /// # use http_net::{ClientConn, ClientEvent, Request};
1376    /// # let mut conn = ClientConn::new();
1377    /// # conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new()).unwrap();
1378    /// # let _ = conn.feed(b"HTTP/1.1 200 \r\nContent-Length: 0\r\n\r\n");
1379    /// # let ClientEvent::Response(resp) = conn.poll().unwrap() else { panic!() };
1380    /// assert_eq!(resp.reason(), "");
1381    /// ```
1382    #[must_use]
1383    pub fn reason(&self) -> &'a str {
1384        core::str::from_utf8(self.info.reason.get(self.head)).unwrap_or("")
1385    }
1386
1387    /// The protocol version.
1388    ///
1389    /// # Examples
1390    ///
1391    /// ```
1392    /// # use http_net::{ClientConn, ClientEvent, Request, Version};
1393    /// # let mut conn = ClientConn::new();
1394    /// # conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new()).unwrap();
1395    /// # let _ = conn.feed(b"HTTP/1.0 200 OK\r\n\r\n");
1396    /// # let ClientEvent::Response(resp) = conn.poll().unwrap() else { panic!() };
1397    /// assert_eq!(resp.version(), Version::Http10);
1398    /// ```
1399    #[must_use]
1400    pub fn version(&self) -> Version {
1401        self.info.version
1402    }
1403
1404    /// All fields in arrival order.
1405    ///
1406    /// # Examples
1407    ///
1408    /// ```
1409    /// # use http_net::{ClientConn, ClientEvent, Request};
1410    /// # let mut conn = ClientConn::new();
1411    /// # conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new()).unwrap();
1412    /// # let _ = conn.feed(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\nX: y\r\n\r\n");
1413    /// # let ClientEvent::Response(resp) = conn.poll().unwrap() else { panic!() };
1414    /// assert_eq!(resp.headers().count(), 2);
1415    /// ```
1416    #[must_use]
1417    pub fn headers(&self) -> Headers<'a> {
1418        Headers {
1419            head: self.head,
1420            fields: self.fields.iter(),
1421        }
1422    }
1423
1424    /// The value of the first field named `name` (ASCII case-insensitive).
1425    ///
1426    /// # Examples
1427    ///
1428    /// ```
1429    /// # use http_net::{ClientConn, ClientEvent, Request};
1430    /// # let mut conn = ClientConn::new();
1431    /// # conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new()).unwrap();
1432    /// # let _ = conn.feed(b"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 0\r\n\r\n");
1433    /// # let ClientEvent::Response(resp) = conn.poll().unwrap() else { panic!() };
1434    /// assert_eq!(resp.header("content-type"), Some(&b"text/plain"[..]));
1435    /// ```
1436    #[must_use]
1437    pub fn header(&self, name: &str) -> Option<&'a [u8]> {
1438        find_header(self.head, self.fields, name)
1439    }
1440
1441    /// Like [`header`](Self::header), but only when the value is UTF-8.
1442    ///
1443    /// # Examples
1444    ///
1445    /// ```
1446    /// # use http_net::{ClientConn, ClientEvent, Request};
1447    /// # let mut conn = ClientConn::new();
1448    /// # conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new()).unwrap();
1449    /// # let _ = conn.feed(b"HTTP/1.1 200 OK\r\nServer: x\r\nContent-Length: 0\r\n\r\n");
1450    /// # let ClientEvent::Response(resp) = conn.poll().unwrap() else { panic!() };
1451    /// assert_eq!(resp.header_str("server"), Some("x"));
1452    /// ```
1453    #[must_use]
1454    pub fn header_str(&self, name: &str) -> Option<&'a str> {
1455        self.header(name).and_then(|v| core::str::from_utf8(v).ok())
1456    }
1457
1458    /// The values of every field named `name`, in arrival order. Each
1459    /// `Set-Cookie` is its own field (they are never comma-joined), so
1460    /// this is how a client reads all of them.
1461    ///
1462    /// # Examples
1463    ///
1464    /// ```
1465    /// # use http_net::{ClientConn, ClientEvent, Request};
1466    /// # let mut conn = ClientConn::new();
1467    /// # conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new()).unwrap();
1468    /// # 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");
1469    /// # let ClientEvent::Response(resp) = conn.poll().unwrap() else { panic!() };
1470    /// let cookies: Vec<&[u8]> = resp.header_all("set-cookie").collect();
1471    /// assert_eq!(cookies, [&b"a=1"[..], &b"b=2; Path=/"[..]]);
1472    /// ```
1473    #[must_use]
1474    pub fn header_all<'n>(&self, name: &'n str) -> HeaderValues<'a, 'n> {
1475        header_values(self.head, self.fields, name)
1476    }
1477
1478    /// The declared `Content-Length`, if the response has one, whatever the
1479    /// framing rules made of it (a `HEAD` response's length is reported
1480    /// here while [`framing`](Self::framing) is `None`).
1481    ///
1482    /// # Examples
1483    ///
1484    /// ```
1485    /// # use http_net::{BodyFraming, ClientConn, ClientEvent, Request};
1486    /// # let mut conn = ClientConn::new();
1487    /// # conn.send_request(&Request::head("/").header("host", "a"), &mut Vec::new()).unwrap();
1488    /// # let _ = conn.feed(b"HTTP/1.1 200 OK\r\nContent-Length: 1234\r\n\r\n");
1489    /// # let ClientEvent::Response(resp) = conn.poll().unwrap() else { panic!() };
1490    /// assert_eq!(resp.framing(), BodyFraming::None);
1491    /// assert_eq!(resp.content_length(), Some(1234));
1492    /// ```
1493    #[must_use]
1494    pub fn content_length(&self) -> Option<u64> {
1495        self.info.content_length
1496    }
1497
1498    /// How the response body is delimited (accounts for `HEAD` requests and
1499    /// bodiless status codes).
1500    ///
1501    /// # Examples
1502    ///
1503    /// ```
1504    /// # use http_net::{BodyFraming, ClientConn, ClientEvent, Request};
1505    /// # let mut conn = ClientConn::new();
1506    /// # conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new()).unwrap();
1507    /// # let _ = conn.feed(b"HTTP/1.1 200 OK\r\n\r\n");
1508    /// # let ClientEvent::Response(resp) = conn.poll().unwrap() else { panic!() };
1509    /// assert_eq!(resp.framing(), BodyFraming::UntilClose);
1510    /// ```
1511    #[must_use]
1512    pub fn framing(&self) -> BodyFraming {
1513        self.info.framing
1514    }
1515
1516    /// Whether the connection can carry another request after this response.
1517    ///
1518    /// # Examples
1519    ///
1520    /// ```
1521    /// # use http_net::{ClientConn, ClientEvent, Request};
1522    /// # let mut conn = ClientConn::new();
1523    /// # conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new()).unwrap();
1524    /// # let _ = conn.feed(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
1525    /// # let ClientEvent::Response(resp) = conn.poll().unwrap() else { panic!() };
1526    /// assert!(resp.keep_alive());
1527    /// ```
1528    #[must_use]
1529    pub fn keep_alive(&self) -> bool {
1530        self.info.keep_alive
1531    }
1532}
1533
1534/// Shows status, reason, version, fields, and framing; the values of
1535/// credential fields (`set-cookie` above all) are replaced by
1536/// `<redacted N bytes>`.
1537impl fmt::Debug for ResponseHead<'_> {
1538    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1539        f.debug_struct("ResponseHead")
1540            .field("status", &self.status())
1541            .field("reason", &self.reason())
1542            .field("version", &self.version())
1543            .field("headers", &self.headers())
1544            .field("framing", &self.framing())
1545            .finish()
1546    }
1547}
1548
1549#[cfg(test)]
1550mod tests {
1551    use super::*;
1552
1553    fn scan(msg: &[u8], kind: HeadKind) -> Result<(HeadScanner, usize), HttpError> {
1554        let mut s = HeadScanner::new();
1555        match s.advance(msg, &Limits::new(), kind, true)? {
1556            Progress::Done(n) => Ok((s, n)),
1557            Progress::Partial => Err(HttpError::UnexpectedEof),
1558        }
1559    }
1560
1561    #[test]
1562    fn test_scanner_resumes_without_rescanning() {
1563        let msg = b"GET / HTTP/1.1\r\nHost: a\r\n\r\n";
1564        let mut s = HeadScanner::new();
1565        for end in 1..msg.len() {
1566            assert!(matches!(
1567                s.advance(&msg[..end], &Limits::new(), HeadKind::Request, true),
1568                Ok(Progress::Partial)
1569            ));
1570        }
1571        assert!(matches!(
1572            s.advance(msg, &Limits::new(), HeadKind::Request, true),
1573            Ok(Progress::Done(n)) if n == msg.len()
1574        ));
1575        assert_eq!(s.fields.len(), 1);
1576    }
1577
1578    #[test]
1579    fn test_scanner_rejects_line_shapes() {
1580        let cases: &[(&[u8], HttpError)] = &[
1581            (b"GET / HTTP/1.1\nHost: a\r\n\r\n", HttpError::BareLf),
1582            (b"GET / HTTP/1.1\r\n Host: a\r\n\r\n", HttpError::ObsFold),
1583            (
1584                b"GET / HTTP/1.1\r\nHost : a\r\n\r\n",
1585                HttpError::WhitespaceBeforeColon,
1586            ),
1587            (
1588                b"GET / HTTP/1.1\r\nHost\r\n\r\n",
1589                HttpError::InvalidHeaderName,
1590            ),
1591            (
1592                b"GET / HTTP/1.1\r\n: a\r\n\r\n",
1593                HttpError::InvalidHeaderName,
1594            ),
1595            (b"GET / HTTP/1.1\r\nHo\rst: a\r\n\r\n", HttpError::BareCr),
1596            (b"GET / HTTP/1.1\r\nHost: a\rb\r\n\r\n", HttpError::BareCr),
1597            (b"GET /\r HTTP/1.1\r\n\r\n", HttpError::BareCr),
1598            (
1599                b"GET / HTTP/1.1\r\nHost: a\x00\r\n\r\n",
1600                HttpError::InvalidHeaderValue,
1601            ),
1602        ];
1603        for (msg, err) in cases {
1604            assert_eq!(scan(msg, HeadKind::Request).err(), Some(*err), "{msg:?}");
1605        }
1606    }
1607
1608    #[test]
1609    fn test_scanner_skips_leading_empty_lines_for_requests_only() {
1610        assert!(scan(b"\r\n\r\nGET / HTTP/1.1\r\n\r\n", HeadKind::Request).is_ok());
1611        assert_eq!(
1612            scan(b"\r\nHTTP/1.1 200 OK\r\n\r\n", HeadKind::Response).err(),
1613            Some(HttpError::InvalidStartLine)
1614        );
1615    }
1616
1617    #[test]
1618    fn test_scanner_enforces_limits_at_boundaries() {
1619        let limits = Limits::new().with_max_start_line(16);
1620        // "GET / HTTP/1.1\r\n" is exactly 16 bytes.
1621        let mut s = HeadScanner::new();
1622        assert!(
1623            s.advance(b"GET / HTTP/1.1\r\n\r\n", &limits, HeadKind::Request, true)
1624                .is_ok()
1625        );
1626        let mut s = HeadScanner::new();
1627        assert_eq!(
1628            s.advance(b"GET /a HTTP/1.1\r\n\r\n", &limits, HeadKind::Request, true)
1629                .err(),
1630            Some(HttpError::StartLineTooLong)
1631        );
1632        // Partial input already at the budget fails without waiting for LF.
1633        let mut s = HeadScanner::new();
1634        assert_eq!(
1635            s.advance(b"GET /aaaaaaaaaaaa", &limits, HeadKind::Request, true)
1636                .err(),
1637            Some(HttpError::StartLineTooLong)
1638        );
1639
1640        let limits = Limits::new().with_max_headers(1);
1641        let mut s = HeadScanner::new();
1642        assert_eq!(
1643            s.advance(
1644                b"GET / HTTP/1.1\r\nA: 1\r\nB: 2\r\n\r\n",
1645                &limits,
1646                HeadKind::Request,
1647                true
1648            )
1649            .err(),
1650            Some(HttpError::TooManyHeaders)
1651        );
1652
1653        // "A: 1\r\n" is 6 bytes; with 8 the final CRLF still fits.
1654        let limits = Limits::new().with_max_header_bytes(8);
1655        let mut s = HeadScanner::new();
1656        assert!(
1657            s.advance(
1658                b"GET / HTTP/1.1\r\nA: 1\r\n\r\n",
1659                &limits,
1660                HeadKind::Request,
1661                true
1662            )
1663            .is_ok()
1664        );
1665        let mut s = HeadScanner::new();
1666        assert_eq!(
1667            s.advance(
1668                b"GET / HTTP/1.1\r\nA: 12\r\nB: 1\r\n\r\n",
1669                &limits,
1670                HeadKind::Request,
1671                true
1672            )
1673            .err(),
1674            Some(HttpError::HeadersTooLarge)
1675        );
1676    }
1677
1678    fn response(line: &[u8]) -> Result<ResponseInfo, HttpError> {
1679        let mut msg = line.to_vec();
1680        msg.extend_from_slice(b"\r\nContent-Length: 0\r\n\r\n");
1681        let (s, _) = scan(&msg, HeadKind::Response)?;
1682        interpret_response(
1683            &msg,
1684            s.start_line(),
1685            &s.fields,
1686            &Limits::new(),
1687            Asked {
1688                head: false,
1689                connect: false,
1690            },
1691        )
1692    }
1693
1694    #[test]
1695    fn test_status_line_without_space_after_code_is_accepted() {
1696        let info = response(b"HTTP/1.1 200").unwrap();
1697        assert_eq!(info.status, 200);
1698        assert_eq!(info.reason, Span::new(12, 12));
1699        assert_eq!(response(b"HTTP/1.1 200 ").unwrap().status, 200);
1700        assert_eq!(response(b"HTTP/1.1 204 No Content").unwrap().status, 204);
1701        assert_eq!(
1702            response(b"HTTP/1.1 20").err(),
1703            Some(HttpError::InvalidStartLine)
1704        );
1705        assert_eq!(
1706            response(b"HTTP/1.1 2000").err(),
1707            Some(HttpError::InvalidStartLine)
1708        );
1709        assert_eq!(
1710            response(b"HTTP/1.1200 OK").err(),
1711            Some(HttpError::InvalidStartLine)
1712        );
1713        assert_eq!(
1714            response(b"HTTP/1.1 600").err(),
1715            Some(HttpError::InvalidStatus)
1716        );
1717    }
1718
1719    #[test]
1720    fn test_protocol_matching_rules() {
1721        assert!(protocol_matches(b"websocket", b"websocket"));
1722        assert!(protocol_matches(b"WebSocket", b"websocket"));
1723        assert!(protocol_matches(b"websocket", b"websocket/13"));
1724        assert!(protocol_matches(b"foo/2", b"FOO/2"));
1725        assert!(!protocol_matches(b"foo/2", b"foo/3"));
1726        assert!(!protocol_matches(b"foo/2", b"foo"));
1727        assert!(!protocol_matches(b"ws", b"websocket"));
1728        assert!(!protocol_matches(b"h2c", b"h2c"));
1729        assert!(!protocol_matches(b"H2C", b"h2c"));
1730        assert!(is_h2c(b"h2c/1") && is_h2c(b"H2c") && !is_h2c(b"h2"));
1731    }
1732
1733    #[test]
1734    fn test_authority_targets_refuse_empty_host_and_bad_escapes() {
1735        assert!(check_target(Method::Connect, b"a%41:443").is_ok());
1736        assert!(check_target(Method::Connect, b"a%4:443").is_err());
1737        assert!(check_target(Method::Get, b"http://a%zz/").is_err());
1738        assert!(check_target(Method::Get, b"http://a%41b/").is_ok());
1739        assert!(check_target(Method::Get, b"ftp://h/").is_ok());
1740    }
1741
1742    #[test]
1743    fn test_versions() {
1744        assert_eq!(parse_version(b"HTTP/1.1"), Ok(Version::Http11));
1745        assert_eq!(parse_version(b"HTTP/1.0"), Ok(Version::Http10));
1746        assert_eq!(parse_version(b"HTTP/1.9"), Ok(Version::Http11));
1747        assert_eq!(
1748            parse_version(b"HTTP/2.0"),
1749            Err(HttpError::UnsupportedVersion)
1750        );
1751        assert_eq!(
1752            parse_version(b"HTTP/0.9"),
1753            Err(HttpError::UnsupportedVersion)
1754        );
1755        assert_eq!(parse_version(b"http/1.1"), Err(HttpError::InvalidVersion));
1756        assert_eq!(parse_version(b"HTTP/1.10"), Err(HttpError::InvalidVersion));
1757        assert_eq!(parse_version(b"HTTP/1"), Err(HttpError::InvalidVersion));
1758    }
1759
1760    #[test]
1761    fn test_target_forms_follow_method() {
1762        assert!(check_target(Method::Get, b"/").is_ok());
1763        assert!(check_target(Method::Options, b"*").is_ok());
1764        assert!(check_target(Method::Get, b"*").is_err());
1765        assert!(check_target(Method::Connect, b"example.com:443").is_ok());
1766        assert!(check_target(Method::Connect, b"[::1]:443").is_ok());
1767        assert!(check_target(Method::Connect, b"example.com").is_err());
1768        assert!(check_target(Method::Connect, b"/").is_err());
1769        assert!(check_target(Method::Get, b"http://example.com/a?b").is_ok());
1770        assert!(check_target(Method::Get, b"http://[::1]:80/").is_ok());
1771        assert!(check_target(Method::Get, b"http://x").is_ok());
1772        assert!(check_target(Method::Get, b"http://x?q").is_ok());
1773        assert!(check_target(Method::Get, b"http://u@x/").is_err());
1774        assert!(check_target(Method::Get, b"http:x").is_err());
1775        assert!(check_target(Method::Get, b"http://:80/").is_err());
1776        assert!(check_target(Method::Get, b"http://x/[a]").is_err());
1777        assert!(check_target(Method::Connect, b":443").is_err());
1778        assert!(check_target(Method::Get, b"1http://x").is_err());
1779        assert!(check_target(Method::Get, b"example.com").is_err());
1780        assert!(check_target(Method::Get, b"").is_err());
1781    }
1782}