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}