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