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