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