http_net/client.rs
1//! The client side of an HTTP/1.1 connection, without I/O.
2
3use alloc::vec::Vec;
4
5use crate::{
6 body::{Decoder, Encoder, Step, push_framing},
7 buffer::Buffer,
8 error::HttpError,
9 head::{
10 Asked, HeadKind, HeadScanner, Headers, ResponseHead, ResponseInfo, Scan,
11 drop_forbidden_trailers, interpret_response, switches_to_offered,
12 },
13 limits::{Limits, MAX_PIPELINE_DEPTH},
14 message::{OutBody, Request, check_trailers, push_last_chunk_with_trailers},
15 method::Method,
16 scan::find_byte,
17 server::Progress,
18};
19
20/// The error for sending a body the server declined (a final status in
21/// answer to `Expect: 100-continue`).
22const REFUSED: HttpError = HttpError::InvalidState(
23 "the server answered with a final status instead of 100 Continue; \
24 the body must not be sent",
25);
26
27/// What a [`ClientConn`] has for the caller after [`poll`](ClientConn::poll).
28///
29/// # Examples
30///
31/// ```
32/// use http_net::{ClientConn, ClientEvent, Request};
33///
34/// let mut conn = ClientConn::new();
35/// let mut out = Vec::new();
36/// conn.send_request(&Request::get("/").header("host", "example.com"), &mut out).unwrap();
37/// let _ = conn.feed(b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n3\r\nabc\r\n0\r\n\r\n");
38/// let mut body = Vec::new();
39/// loop {
40/// match conn.poll().unwrap() {
41/// ClientEvent::Response(resp) => assert_eq!(resp.status(), 200),
42/// ClientEvent::Body(chunk) => body.extend_from_slice(chunk),
43/// ClientEvent::ResponseEnd => break,
44/// other => panic!("{other:?}"),
45/// }
46/// }
47/// assert_eq!(body, b"abc");
48/// ```
49#[non_exhaustive]
50pub enum ClientEvent<'a> {
51 /// The final response head arrived. Its body follows as
52 /// [`Body`](ClientEvent::Body) events, then
53 /// [`ResponseEnd`](ClientEvent::ResponseEnd). Interim (1xx) responses
54 /// before it are skipped unless
55 /// [`set_report_informational`](ClientConn::set_report_informational)
56 /// asked for them; either way they count against
57 /// [`Limits::max_interim`](crate::Limits::max_interim) and, together,
58 /// [`Limits::max_header_bytes`](crate::Limits::max_header_bytes).
59 Response(ResponseHead<'a>),
60 /// An interim response other than `101` (for example `103 Early Hints`,
61 /// or `102 Processing`), only with
62 /// [`set_report_informational(true)`](ClientConn::set_report_informational).
63 /// The final response follows.
64 Informational(ResponseHead<'a>),
65 /// The server answered `100 Continue` to a request built with
66 /// [`Request::expect_continue`](crate::Request::expect_continue): send
67 /// the body now.
68 Continue,
69 /// A run of body bytes, after chunked decoding.
70 Body(&'a [u8]),
71 /// The response's trailer fields, just before
72 /// [`ResponseEnd`](ClientEvent::ResponseEnd): only with
73 /// [`set_report_trailers(true)`](ClientConn::set_report_trailers) and a
74 /// non-empty trailer section.
75 Trailers(Headers<'a>),
76 /// The response is complete.
77 ResponseEnd,
78 /// More input is needed (or, for a body that runs until close,
79 /// [`feed_eof`](ClientConn::feed_eof) once the server closes).
80 NeedInput,
81 /// No request is outstanding; send the next one.
82 Idle,
83 /// The server switched protocols (101 to an upgrade request, or 2xx to
84 /// `CONNECT`); the response head was reported just before. Bytes of the
85 /// new protocol that already arrived are in
86 /// [`remaining_input`](ClientConn::remaining_input).
87 Upgraded,
88 /// The connection cannot carry another request: close the socket.
89 Closed,
90}
91
92/// Body bytes print as their length: a body can carry credentials that no
93/// field redaction sees (a token in a JSON answer).
94impl core::fmt::Debug for ClientEvent<'_> {
95 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
96 match self {
97 Self::Response(head) => f.debug_tuple("Response").field(head).finish(),
98 Self::Informational(head) => f.debug_tuple("Informational").field(head).finish(),
99 Self::Continue => f.write_str("Continue"),
100 Self::Body(bytes) => f
101 .debug_tuple("Body")
102 .field(&format_args!("{} bytes", bytes.len()))
103 .finish(),
104 Self::Trailers(fields) => f.debug_tuple("Trailers").field(fields).finish(),
105 Self::ResponseEnd => f.write_str("ResponseEnd"),
106 Self::NeedInput => f.write_str("NeedInput"),
107 Self::Idle => f.write_str("Idle"),
108 Self::Upgraded => f.write_str("Upgraded"),
109 Self::Closed => f.write_str("Closed"),
110 }
111 }
112}
113
114/// Opt-in tolerances for responses a strict client would refuse. All are
115/// off by default; requests are never parsed leniently.
116///
117/// - `obs_fold_in_responses`: replace each obsolete line fold in a response
118/// head (a field line continued on the next line, which begins with a
119/// space or tab) by overwriting the fold's CR and LF with spaces, as RFC
120/// 9112 section 5.2 tells a user agent to do. The rewrite happens in
121/// place in the connection's buffer, so offsets do not move, views stay
122/// borrowed, and nothing is allocated. Whitespace before the first field
123/// is still refused.
124///
125/// Bare LF, bare CR, `Content-Length` with `Transfer-Encoding` on a
126/// response that has a body, and whitespace before a colon are never
127/// tolerated: they are how messages get framed differently by different
128/// hops. A response with no body (`HEAD`, 1xx, 204, 304, 2xx to `CONNECT`)
129/// ends at its blank line whatever its framing fields say.
130///
131/// # Examples
132///
133/// ```
134/// use http_net::{ClientConn, ClientEvent, ClientLeniency, Request};
135///
136/// let mut conn = ClientConn::new();
137/// conn.set_leniency(ClientLeniency::new().with_obs_fold_in_responses(true));
138/// conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new())?;
139/// let _ = conn.feed(b"HTTP/1.1 200 OK\r\nX-Long: part one,\r\n part two\r\nContent-Length: 0\r\n\r\n");
140/// let ClientEvent::Response(resp) = conn.poll()? else { panic!() };
141/// assert_eq!(resp.header("x-long"), Some(&b"part one, part two"[..]));
142/// # Ok::<(), http_net::HttpError>(())
143/// ```
144#[non_exhaustive]
145#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
146pub struct ClientLeniency {
147 /// Replace obs-folds in response fields with spaces instead of refusing
148 /// them with [`HttpError::ObsFold`].
149 pub obs_fold_in_responses: bool,
150}
151
152impl ClientLeniency {
153 /// Every tolerance off (the strict default).
154 ///
155 /// # Examples
156 ///
157 /// ```
158 /// assert!(!http_net::ClientLeniency::new().obs_fold_in_responses);
159 /// ```
160 #[must_use]
161 pub const fn new() -> Self {
162 Self {
163 obs_fold_in_responses: false,
164 }
165 }
166
167 /// Sets [`obs_fold_in_responses`](Self::obs_fold_in_responses).
168 ///
169 /// # Examples
170 ///
171 /// ```
172 /// let l = http_net::ClientLeniency::new().with_obs_fold_in_responses(true);
173 /// assert!(l.obs_fold_in_responses);
174 /// ```
175 #[must_use]
176 pub const fn with_obs_fold_in_responses(mut self, on: bool) -> Self {
177 self.obs_fold_in_responses = on;
178 self
179 }
180}
181
182/// The in-place obs-fold rewrite of one response head. It walks the head's
183/// line ends once, overall linear however the input is split, and tells the
184/// head scanner how much it may see: a line end whose next byte has not
185/// arrived is held back, because only that byte says whether the line is
186/// folded. The scanner therefore never completes a field line that is about
187/// to be continued.
188#[derive(Clone, Copy, Debug)]
189struct Unfold {
190 /// Where the search for the next LF resumes.
191 scan: usize,
192 /// Start of the line being examined.
193 line_start: usize,
194 /// The status line has ended (it is never folded).
195 past_status: bool,
196 /// The blank line that ends the head was seen.
197 done: bool,
198}
199
200impl Unfold {
201 const fn new() -> Self {
202 Self {
203 scan: 0,
204 line_start: 0,
205 past_status: false,
206 done: false,
207 }
208 }
209
210 /// Rewrites the folds found in `msg` (the head so far) and returns how
211 /// many of its bytes the head scanner may examine.
212 fn run(&mut self, msg: &mut [u8], eof: bool) -> usize {
213 if self.done {
214 return msg.len();
215 }
216 loop {
217 let Some(off) = msg.get(self.scan..).and_then(|rest| find_byte(b'\n', rest)) else {
218 self.scan = msg.len();
219 return msg.len();
220 };
221 let lf = self.scan + off;
222 if !self.past_status {
223 self.past_status = true;
224 self.line_start = lf + 1;
225 self.scan = lf + 1;
226 continue;
227 }
228 // line_start <= lf: it only ever moves to just past an LF.
229 let line = msg.get(self.line_start..lf).unwrap_or(&[]);
230 if line.is_empty() || line == b"\r" {
231 self.done = true;
232 return msg.len();
233 }
234 let crlf = lf > 0 && msg.get(lf - 1) == Some(&b'\r');
235 match msg.get(lf + 1) {
236 None if eof => {
237 self.scan = msg.len();
238 return msg.len();
239 }
240 None => {
241 // Revisit this LF once its next byte arrives.
242 self.scan = lf;
243 return lf;
244 }
245 Some(&b) if crlf && (b == b' ' || b == b'\t') => {
246 // RFC 9112 section 5.2: replace the fold with SP. The
247 // line goes on, so its start stays where it was.
248 msg[lf - 1] = b' ';
249 msg[lf] = b' ';
250 self.scan = lf + 1;
251 }
252 Some(_) => {
253 self.line_start = lf + 1;
254 self.scan = lf + 1;
255 }
256 }
257 }
258 }
259}
260
261#[derive(Clone, Copy, Debug, PartialEq, Eq)]
262enum State {
263 Idle,
264 Head,
265 /// An interim head was reported and is still pinned; the next poll
266 /// drops it and waits for the next head.
267 Interim,
268 Body,
269 Complete,
270 Upgraded,
271 Closed,
272}
273
274#[derive(Clone, Copy, Debug, PartialEq, Eq)]
275enum ReqWrite {
276 Streaming(Encoder),
277 Sent,
278}
279
280/// The client side of one HTTP/1.1 connection: a sans-IO state machine.
281///
282/// By default one request is outstanding at a time; with
283/// [`Limits::with_pipeline_depth`], idempotent requests may be pipelined
284/// behind it (see [`can_send`](Self::can_send)).
285/// [`send_request`](Self::send_request)
286/// serializes a [`Request`]; [`feed`](Self::feed) and [`poll`](Self::poll)
287/// parse the response. When the response allows it, the connection returns
288/// to [`ClientEvent::Idle`] and can send the next request (keep-alive).
289///
290/// The response is framed with the request in mind: a `HEAD` request's
291/// response has no body whatever its headers say, a `CONNECT` 2xx becomes a
292/// tunnel, and a response with neither `Content-Length` nor
293/// `Transfer-Encoding` runs until the server closes.
294///
295/// # Examples
296///
297/// ```
298/// use http_net::{ClientConn, ClientEvent, Request};
299///
300/// let mut conn = ClientConn::new();
301/// let mut out = Vec::new();
302/// conn.send_request(&Request::get("/a").header("host", "h"), &mut out).unwrap();
303/// assert_eq!(out, b"GET /a HTTP/1.1\r\nhost: h\r\n\r\n");
304/// let _ = conn.feed(b"HTTP/1.1 204 No Content\r\n\r\n");
305/// assert!(matches!(conn.poll().unwrap(), ClientEvent::Response(_)));
306/// assert!(matches!(conn.poll().unwrap(), ClientEvent::ResponseEnd));
307/// assert!(matches!(conn.poll().unwrap(), ClientEvent::Idle));
308/// ```
309pub struct ClientConn {
310 buf: Buffer,
311 limits: Limits,
312 scanner: HeadScanner,
313 trailers: HeadScanner,
314 decoder: Decoder,
315 info: ResponseInfo,
316 asked: Asked,
317 upgrade_asked: bool,
318 /// The protocols the outstanding upgrade request offered, comma
319 /// separated; a `101` may switch only to these.
320 upgrade_offer: Vec<u8>,
321 state: State,
322 req: ReqWrite,
323 keep_alive: bool,
324 /// Bytes of interim (1xx) heads seen for the current request. They
325 /// count against the header budget so that a server cannot keep the
326 /// client parsing forever without answering.
327 interim_bytes: usize,
328 /// Interim heads seen for the current request, against `max_interim`.
329 interim_count: u16,
330 report_interim: bool,
331 report_trailers: bool,
332 /// The request was sent with `Expect: 100-continue` and neither a `100`,
333 /// a final response, nor body bytes have happened since.
334 waiting_continue: bool,
335 /// A final response arrived while waiting for `100`: the promised body
336 /// must not be sent, and the connection closes after the response.
337 body_refused: bool,
338 /// The request body was declared chunked (trailers allowed).
339 req_chunked: bool,
340 /// The request being answered is idempotent (it may share the
341 /// connection with pipelined requests).
342 idempotent: bool,
343 /// Requests sent after the one being answered, in order: a fixed ring,
344 /// so pipelining allocates nothing after construction.
345 queue: [Queued; MAX_PIPELINE_DEPTH as usize],
346 queue_start: usize,
347 queued: usize,
348 /// The connection failed while a response was being read, so that
349 /// request stays unanswered: `outstanding()` still counts it.
350 response_failed: bool,
351 leniency: ClientLeniency,
352 /// Where the obs-fold rewrite stopped in the current response head.
353 unfold: Unfold,
354}
355
356/// A pipelined request waiting for its response: what framing its response
357/// needs and whether it asked to close.
358#[derive(Clone, Copy, Debug)]
359struct Queued {
360 asked: Asked,
361 close: bool,
362}
363
364/// Writes the request line, fields, framing, and (for a full body) the body;
365/// returns how the rest of the body, if any, is to be written.
366fn write_request(request: &Request, out: &mut Vec<u8>) -> Result<ReqWrite, HttpError> {
367 let method = request.method_kind;
368 let expects_body = matches!(method, Method::Post | Method::Put | Method::Patch);
369 let (mut encoder, declared) = match &request.body {
370 OutBody::Full(b) if b.is_empty() && !expects_body => (Encoder::Discard, None),
371 OutBody::Full(b) => (
372 Encoder::Length {
373 remaining: b.len() as u64,
374 },
375 Some(b.len() as u64),
376 ),
377 OutBody::Length(n) => (Encoder::Length { remaining: *n }, Some(*n)),
378 OutBody::Chunked => (Encoder::Chunked, None),
379 };
380 out.extend_from_slice(request.method().as_bytes());
381 out.push(b' ');
382 out.extend_from_slice(request.target().as_bytes());
383 out.extend_from_slice(b" HTTP/1.1\r\n");
384 out.extend_from_slice(request.fields());
385 push_framing(out, encoder, declared);
386 if request.close {
387 out.extend_from_slice(b"connection: close\r\n");
388 }
389 out.extend_from_slice(b"\r\n");
390 match &request.body {
391 OutBody::Full(b) => {
392 encoder.write(b, out)?;
393 Ok(ReqWrite::Sent)
394 }
395 _ => Ok(ReqWrite::Streaming(encoder)),
396 }
397}
398
399const NO_QUEUED: Queued = Queued {
400 asked: Asked {
401 head: false,
402 connect: false,
403 },
404 close: false,
405};
406
407impl Default for ClientConn {
408 fn default() -> Self {
409 Self::new()
410 }
411}
412
413impl core::fmt::Debug for ClientConn {
414 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
415 f.debug_struct("ClientConn")
416 .field("state", &self.state)
417 .field("request", &self.req)
418 .field("buffered", &self.buf.unread().len())
419 .field("limits", &self.limits)
420 .finish()
421 }
422}
423
424impl ClientConn {
425 /// A connection with the default [`Limits`].
426 ///
427 /// # Examples
428 ///
429 /// ```
430 /// let mut conn = http_net::ClientConn::new();
431 /// assert!(matches!(conn.poll(), Ok(http_net::ClientEvent::Idle)));
432 /// ```
433 #[must_use]
434 pub fn new() -> Self {
435 Self::with_limits(Limits::new())
436 }
437
438 /// A connection enforcing `limits` on responses.
439 ///
440 /// # Examples
441 ///
442 /// ```
443 /// let conn = http_net::ClientConn::with_limits(http_net::Limits::new().with_max_body(1 << 24));
444 /// assert_eq!(conn.limits().max_body(), 1 << 24);
445 /// ```
446 #[must_use]
447 pub fn with_limits(limits: Limits) -> Self {
448 Self::from_buffer(Buffer::new(limits.buffer_capacity()), limits)
449 }
450
451 /// A connection enforcing `limits` that reads into `storage` (its
452 /// capacity is used up to [`Limits::buffer_capacity`], its contents are
453 /// ignored), for drivers that pool buffers. Give it back with
454 /// [`into_buffer`](Self::into_buffer).
455 ///
456 /// # Examples
457 ///
458 /// ```
459 /// let conn = http_net::ClientConn::with_buffer(Vec::with_capacity(4096), http_net::Limits::new());
460 /// assert!(conn.into_buffer().capacity() >= 4096);
461 /// ```
462 #[must_use]
463 pub fn with_buffer(storage: Vec<u8>, limits: Limits) -> Self {
464 Self::from_buffer(
465 Buffer::with_storage(storage, limits.buffer_capacity()),
466 limits,
467 )
468 }
469
470 fn from_buffer(buf: Buffer, limits: Limits) -> Self {
471 Self {
472 buf,
473 limits,
474 scanner: HeadScanner::new(),
475 trailers: HeadScanner::new(),
476 decoder: Decoder::new(),
477 info: ResponseInfo::EMPTY,
478 asked: Asked {
479 head: false,
480 connect: false,
481 },
482 upgrade_asked: false,
483 upgrade_offer: Vec::new(),
484 state: State::Idle,
485 req: ReqWrite::Sent,
486 keep_alive: true,
487 interim_bytes: 0,
488 interim_count: 0,
489 report_interim: false,
490 report_trailers: false,
491 waiting_continue: false,
492 body_refused: false,
493 req_chunked: false,
494 idempotent: true,
495 queue: [NO_QUEUED; MAX_PIPELINE_DEPTH as usize],
496 queue_start: 0,
497 queued: 0,
498 response_failed: false,
499 leniency: ClientLeniency::new(),
500 unfold: Unfold::new(),
501 }
502 }
503
504 /// The limits this connection enforces.
505 ///
506 /// # Examples
507 ///
508 /// ```
509 /// assert_eq!(http_net::ClientConn::new().limits().max_headers(), 100);
510 /// ```
511 #[must_use]
512 pub fn limits(&self) -> &Limits {
513 &self.limits
514 }
515
516 /// Copies as much of `bytes` into the input buffer as fits; returns how
517 /// many were taken (see [`ServerConn::feed`](crate::ServerConn::feed)).
518 ///
519 /// # Examples
520 ///
521 /// ```
522 /// let mut conn = http_net::ClientConn::new();
523 /// assert_eq!(conn.feed(b"HTTP/1.1"), 8);
524 /// ```
525 #[must_use = "bytes beyond the returned count were not taken"]
526 pub fn feed(&mut self, bytes: &[u8]) -> usize {
527 self.buf.feed(bytes)
528 }
529
530 /// Spare space at the end of the input buffer; follow with
531 /// [`commit`](Self::commit).
532 ///
533 /// # Examples
534 ///
535 /// ```
536 /// let mut conn = http_net::ClientConn::new();
537 /// assert!(!conn.read_buf().is_empty());
538 /// ```
539 pub fn read_buf(&mut self) -> &mut [u8] {
540 self.buf.spare()
541 }
542
543 /// Records that `n` bytes were written into [`read_buf`](Self::read_buf).
544 /// An `n` larger than the slice `read_buf` returned is clamped to its
545 /// length: committing bytes that were not written would hand stale
546 /// buffer contents to the parser.
547 ///
548 /// # Examples
549 ///
550 /// ```
551 /// let mut conn = http_net::ClientConn::new();
552 /// conn.read_buf()[..4].copy_from_slice(b"HTTP");
553 /// conn.commit(4);
554 /// ```
555 pub fn commit(&mut self, n: usize) {
556 self.buf.commit(n);
557 }
558
559 /// Records that the server closed its sending side: ends a body that
560 /// runs until close, or reports [`HttpError::UnexpectedEof`] mid-message.
561 ///
562 /// # Examples
563 ///
564 /// ```
565 /// use http_net::{ClientConn, ClientEvent, Request};
566 ///
567 /// let mut conn = ClientConn::new();
568 /// conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new()).unwrap();
569 /// let _ = conn.feed(b"HTTP/1.0 200 OK\r\n\r\nall of it");
570 /// assert!(matches!(conn.poll().unwrap(), ClientEvent::Response(_)));
571 /// assert!(matches!(conn.poll().unwrap(), ClientEvent::Body(b"all of it")));
572 /// assert!(matches!(conn.poll().unwrap(), ClientEvent::NeedInput));
573 /// conn.feed_eof();
574 /// assert!(matches!(conn.poll().unwrap(), ClientEvent::ResponseEnd));
575 /// assert!(matches!(conn.poll().unwrap(), ClientEvent::Closed));
576 /// ```
577 pub fn feed_eof(&mut self) {
578 self.buf.set_eof();
579 }
580
581 /// Writes `request` into `out` and starts waiting for its response.
582 ///
583 /// A full body is sent with `content-length` (omitted for an empty body
584 /// on methods that do not expect one); a streamed body continues with
585 /// [`send_body`](Self::send_body) and [`end_body`](Self::end_body).
586 ///
587 /// A request built with [`Request::expect_continue`] is sent head first:
588 /// [`is_waiting_for_continue`](Self::is_waiting_for_continue) is true
589 /// until the server answers `100 Continue` (reported as
590 /// [`ClientEvent::Continue`]: then send the body), a final status (then
591 /// the body must not be sent and the connection closes after the
592 /// response), or the caller sends the body anyway (RFC 9110 section
593 /// 10.1.1 allows it after a wait of its choosing).
594 ///
595 /// # Errors
596 ///
597 /// - the first error recorded by the [`Request`] builder;
598 /// - [`HttpError::MissingHost`] when the request has no `host` header;
599 /// - [`HttpError::InvalidState`] when a request is outstanding and this
600 /// one cannot be pipelined (depth 1, depth full, or not idempotent with
601 /// a full body; see [`can_send`](Self::can_send)), when the connection
602 /// is closed or upgraded, or when an `expect_continue` request has a
603 /// full body (it needs `content_length` or `chunked`);
604 /// - [`HttpError::UnexpectedData`] when response bytes nobody asked for
605 /// are buffered.
606 ///
607 /// # Examples
608 ///
609 /// ```
610 /// use http_net::{ClientConn, HttpError, Request};
611 ///
612 /// let mut conn = ClientConn::new();
613 /// let mut out = Vec::new();
614 /// assert_eq!(conn.send_request(&Request::get("/"), &mut out), Err(HttpError::MissingHost));
615 /// conn.send_request(&Request::post("/f").header("host", "a").body(&b"xyz"[..]), &mut out).unwrap();
616 /// assert_eq!(out, b"POST /f HTTP/1.1\r\nhost: a\r\ncontent-length: 3\r\n\r\nxyz");
617 /// ```
618 pub fn send_request(&mut self, request: &Request, out: &mut Vec<u8>) -> Result<(), HttpError> {
619 if let Some(e) = request.error() {
620 return Err(e);
621 }
622 if self.settled() {
623 self.state = State::Idle;
624 }
625 match self.state {
626 State::Idle => {}
627 State::Closed | State::Upgraded => {
628 return Err(HttpError::InvalidState(
629 "the connection is closed or upgraded",
630 ));
631 }
632 _ => return self.send_pipelined(request, out),
633 }
634 if !self.buf.unread().is_empty() {
635 return Err(HttpError::UnexpectedData);
636 }
637 if !request.has_host {
638 return Err(HttpError::MissingHost);
639 }
640 if request.expect_continue && matches!(request.body, OutBody::Full(_)) {
641 // The point of the expectation is to hold the body back until
642 // the server agrees; a full body would be written at once.
643 return Err(HttpError::InvalidState(
644 "expect_continue needs a streamed body (content_length or chunked), \
645 sent after ClientEvent::Continue",
646 ));
647 }
648 let method = request.method_kind;
649 self.req = write_request(request, out)?;
650 self.asked = Asked {
651 head: method == Method::Head,
652 connect: method == Method::Connect,
653 };
654 self.upgrade_asked = request.upgrade;
655 if request.upgrade {
656 self.upgrade_offer.clear();
657 for offer in request.upgrade_offers() {
658 if !self.upgrade_offer.is_empty() {
659 self.upgrade_offer.push(b',');
660 }
661 self.upgrade_offer.extend_from_slice(offer);
662 }
663 }
664 self.keep_alive = !request.close;
665 self.idempotent = method.is_idempotent();
666 self.req_chunked = matches!(request.body, OutBody::Chunked);
667 self.waiting_continue = request.expect_continue;
668 self.body_refused = false;
669 self.buf.wipe_consumed();
670 self.buf.unpin();
671 self.buf.pin_at_pos();
672 self.scanner.reset();
673 self.unfold = Unfold::new();
674 self.interim_bytes = 0;
675 self.interim_count = 0;
676 self.state = State::Head;
677 Ok(())
678 }
679
680 /// Sends `request` behind the ones already outstanding (pipelining).
681 fn send_pipelined(&mut self, request: &Request, out: &mut Vec<u8>) -> Result<(), HttpError> {
682 if !self.can_send() {
683 return Err(HttpError::InvalidState(
684 if self.limits.pipeline_depth() <= 1 {
685 "a request is already outstanding (raise Limits::with_pipeline_depth to pipeline)"
686 } else {
687 "pipelining needs room in the pipeline depth, idempotent requests, \
688 every earlier body sent, and no close, upgrade, CONNECT, or expectation"
689 },
690 ));
691 }
692 if !request.has_host {
693 return Err(HttpError::MissingHost);
694 }
695 let method = request.method_kind;
696 if !method.is_idempotent()
697 || request.upgrade
698 || request.expect_continue
699 || !matches!(request.body, OutBody::Full(_))
700 {
701 return Err(HttpError::InvalidState(
702 "only idempotent requests with a full body and no upgrade or expectation \
703 can be pipelined",
704 ));
705 }
706 let req = write_request(request, out)?;
707 debug_assert_eq!(req, ReqWrite::Sent);
708 let slot = (self.queue_start + self.queued) % self.queue.len();
709 self.queue[slot] = Queued {
710 asked: Asked {
711 head: method == Method::Head,
712 connect: false,
713 },
714 close: request.close,
715 };
716 self.queued += 1;
717 Ok(())
718 }
719
720 /// Starts on the response to the oldest queued request.
721 fn next_queued(&mut self) {
722 let next = self.queue[self.queue_start];
723 self.queue_start = (self.queue_start + 1) % self.queue.len();
724 self.queued -= 1;
725 self.asked = next.asked;
726 self.keep_alive = !next.close;
727 self.upgrade_asked = false;
728 self.idempotent = true;
729 self.req_chunked = false;
730 self.waiting_continue = false;
731 self.body_refused = false;
732 self.buf.wipe_consumed();
733 self.buf.unpin();
734 self.buf.pin_at_pos();
735 self.scanner.reset();
736 self.interim_bytes = 0;
737 self.interim_count = 0;
738 self.state = State::Head;
739 }
740
741 /// Whether any outstanding request asked to close the connection.
742 fn closing(&self) -> bool {
743 !self.keep_alive
744 || (0..self.queued).any(|i| self.queue[(self.queue_start + i) % self.queue.len()].close)
745 }
746
747 /// The number of requests sent whose response has not completed:
748 /// 0 when idle, up to [`Limits::pipeline_depth`] when pipelining. After
749 /// the connection closed it is the number of requests that never got
750 /// a response (send them again on a new connection if they are safe to
751 /// repeat).
752 ///
753 /// # Examples
754 ///
755 /// ```
756 /// use http_net::{ClientConn, Limits, Request};
757 ///
758 /// let mut conn = ClientConn::with_limits(Limits::new().with_pipeline_depth(4));
759 /// let get = |p: &str| Request::get(p).header("host", "a");
760 /// let mut out = Vec::new();
761 /// conn.send_request(&get("/1"), &mut out)?;
762 /// conn.send_request(&get("/2"), &mut out)?;
763 /// assert_eq!(conn.outstanding(), 2);
764 /// # Ok::<(), http_net::HttpError>(())
765 /// ```
766 #[must_use]
767 pub fn outstanding(&self) -> usize {
768 match self.state {
769 State::Idle => 0,
770 State::Head | State::Interim | State::Body => 1 + self.queued,
771 State::Complete | State::Upgraded => self.queued,
772 State::Closed => self.queued + usize::from(self.response_failed),
773 }
774 }
775
776 /// Whether [`send_request`](Self::send_request) would accept another
777 /// request now. With no request outstanding: the connection is open
778 /// and nothing unsolicited is buffered. With requests outstanding
779 /// (pipelining): fewer than [`Limits::pipeline_depth`] are outstanding,
780 /// all of them are idempotent with their bodies fully sent, none asked
781 /// to close, upgrade, `CONNECT`, or expect `100 Continue`, and the
782 /// response being read, once its head has arrived, keeps the connection
783 /// open (RFC 9112 section 9.6: after `close`, or a response that ends
784 /// with the connection, the client sends nothing more). The new request
785 /// must also be idempotent with a full body (RFC 9112 section 9.3.2: a
786 /// client should not pipeline non-idempotent requests).
787 ///
788 /// # Examples
789 ///
790 /// ```
791 /// use http_net::{ClientConn, Limits, Request};
792 ///
793 /// let mut conn = ClientConn::new(); // depth 1: no pipelining
794 /// assert!(conn.can_send());
795 /// conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new())?;
796 /// assert!(!conn.can_send());
797 /// # Ok::<(), http_net::HttpError>(())
798 /// ```
799 #[must_use]
800 pub fn can_send(&self) -> bool {
801 match self.state {
802 State::Complete if self.settled() => self.buf.unread().is_empty() && !self.buf.is_eof(),
803 State::Idle => self.buf.unread().is_empty() && !self.buf.is_eof(),
804 State::Closed | State::Upgraded => false,
805 State::Head | State::Interim | State::Body | State::Complete => {
806 self.outstanding() < usize::from(self.limits.pipeline_depth())
807 && self.idempotent
808 && self.req == ReqWrite::Sent
809 && !self.closing()
810 && !self.upgrade_asked
811 && !self.asked.connect
812 && !self.waiting_continue
813 && !self.body_refused
814 && !self.buf.is_eof()
815 && (matches!(self.state, State::Head | State::Interim) || self.info.keep_alive)
816 }
817 }
818 }
819
820 /// Returns the connection to its initial state for a new socket,
821 /// keeping the buffer and field-index allocations and the limits and
822 /// reporting settings. Requests still outstanding are forgotten.
823 ///
824 /// # Examples
825 ///
826 /// ```
827 /// use http_net::{ClientConn, Progress, Request};
828 ///
829 /// let mut conn = ClientConn::new();
830 /// conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new())?;
831 /// conn.reset();
832 /// assert_eq!(conn.progress(), Progress::Idle);
833 /// assert!(conn.can_send());
834 /// # Ok::<(), http_net::HttpError>(())
835 /// ```
836 pub fn reset(&mut self) {
837 self.buf.reset();
838 self.scanner.reset();
839 self.trailers.reset();
840 self.decoder = Decoder::new();
841 self.info = ResponseInfo::EMPTY;
842 self.asked = NO_QUEUED.asked;
843 self.upgrade_asked = false;
844 self.state = State::Idle;
845 self.req = ReqWrite::Sent;
846 self.keep_alive = true;
847 self.interim_bytes = 0;
848 self.interim_count = 0;
849 self.waiting_continue = false;
850 self.body_refused = false;
851 self.req_chunked = false;
852 self.idempotent = true;
853 self.queue_start = 0;
854 self.queued = 0;
855 self.response_failed = false;
856 }
857
858 /// Gives the input buffer's storage back, for a pool.
859 ///
860 /// # Examples
861 ///
862 /// ```
863 /// assert_eq!(http_net::ClientConn::new().into_buffer().capacity(), 0);
864 /// ```
865 #[must_use]
866 pub fn into_buffer(mut self) -> Vec<u8> {
867 self.buf.take_storage()
868 }
869
870 /// Frees the input buffer and the field indexes while no request is
871 /// outstanding and nothing is buffered. Returns whether memory was
872 /// released.
873 ///
874 /// # Examples
875 ///
876 /// ```
877 /// use http_net::{ClientConn, ClientEvent, Request};
878 ///
879 /// let mut conn = ClientConn::new();
880 /// conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new())?;
881 /// let _ = conn.feed(b"HTTP/1.1 204 No Content\r\n\r\n");
882 /// while !matches!(conn.poll()?, ClientEvent::Idle) {}
883 /// assert!(conn.shrink_idle());
884 /// # Ok::<(), http_net::HttpError>(())
885 /// ```
886 pub fn shrink_idle(&mut self) -> bool {
887 if self.state != State::Idle {
888 return false;
889 }
890 self.buf.unpin();
891 let freed = self.buf.shrink();
892 if freed {
893 self.scanner.fields = Vec::new();
894 self.trailers.fields = Vec::new();
895 }
896 freed
897 }
898
899 /// Writes body bytes of a streamed request.
900 ///
901 /// Sending body bytes while waiting for `100 Continue` ends the wait.
902 ///
903 /// # Errors
904 ///
905 /// [`HttpError::BodyLengthMismatch`] past the declared length;
906 /// [`HttpError::InvalidState`] when no streamed request is in progress,
907 /// or when the server answered an `expect_continue` request with a final
908 /// status (the body must not be sent then).
909 ///
910 /// # Examples
911 ///
912 /// ```
913 /// use http_net::{ClientConn, Request};
914 ///
915 /// let mut conn = ClientConn::new();
916 /// let mut out = Vec::new();
917 /// conn.send_request(&Request::post("/up").header("host", "a").chunked(), &mut out).unwrap();
918 /// conn.send_body(b"part", &mut out).unwrap();
919 /// conn.end_body(&mut out).unwrap();
920 /// assert!(out.ends_with(b"4\r\npart\r\n0\r\n\r\n"));
921 /// ```
922 pub fn send_body(&mut self, data: &[u8], out: &mut Vec<u8>) -> Result<(), HttpError> {
923 if self.body_refused {
924 return Err(REFUSED);
925 }
926 match &mut self.req {
927 ReqWrite::Streaming(encoder) => {
928 encoder.write(data, out)?;
929 self.waiting_continue = false;
930 Ok(())
931 }
932 ReqWrite::Sent => Err(HttpError::InvalidState("no streamed request in progress")),
933 }
934 }
935
936 /// Ends a streamed request body.
937 ///
938 /// # Errors
939 ///
940 /// [`HttpError::BodyLengthMismatch`] when fewer bytes than declared were
941 /// sent; [`HttpError::InvalidState`] when no streamed request is in
942 /// progress, or when the server answered an `expect_continue` request
943 /// with a final status (the body must not be sent then).
944 ///
945 /// # Examples
946 ///
947 /// ```
948 /// use http_net::{ClientConn, Request};
949 ///
950 /// let mut conn = ClientConn::new();
951 /// let mut out = Vec::new();
952 /// conn.send_request(&Request::post("/").header("host", "a").content_length(2), &mut out).unwrap();
953 /// conn.send_body(b"ok", &mut out).unwrap();
954 /// conn.end_body(&mut out).unwrap();
955 /// assert!(out.ends_with(b"\r\n\r\nok"));
956 /// ```
957 pub fn end_body(&mut self, out: &mut Vec<u8>) -> Result<(), HttpError> {
958 self.end_body_with_trailers(&[], out)
959 }
960
961 /// Ends a streamed chunked request body with a trailer section (see
962 /// [`ServerConn::end_body_with_trailers`](crate::ServerConn::end_body_with_trailers)
963 /// for the fields that are refused).
964 ///
965 /// # Errors
966 ///
967 /// [`HttpError::TrailerNotAllowed`] for a forbidden field or a body
968 /// that is not chunked; the field errors of
969 /// [`Request::header`]; otherwise as [`end_body`](Self::end_body).
970 ///
971 /// # Examples
972 ///
973 /// ```
974 /// use http_net::{ClientConn, Request};
975 ///
976 /// let mut conn = ClientConn::new();
977 /// let mut out = Vec::new();
978 /// conn.send_request(&Request::put("/f").header("host", "a").chunked(), &mut out)?;
979 /// conn.send_body(b"data", &mut out)?;
980 /// conn.end_body_with_trailers(&[("content-digest", b"sha-256=:x:")], &mut out)?;
981 /// assert!(out.ends_with(b"0\r\ncontent-digest: sha-256=:x:\r\n\r\n"));
982 /// # Ok::<(), http_net::HttpError>(())
983 /// ```
984 pub fn end_body_with_trailers(
985 &mut self,
986 trailers: &[(&str, &[u8])],
987 out: &mut Vec<u8>,
988 ) -> Result<(), HttpError> {
989 if self.body_refused {
990 return Err(REFUSED);
991 }
992 let ReqWrite::Streaming(encoder) = &mut self.req else {
993 return Err(HttpError::InvalidState("no streamed request in progress"));
994 };
995 if !trailers.is_empty() && !self.req_chunked {
996 return Err(HttpError::TrailerNotAllowed);
997 }
998 if *encoder == Encoder::Chunked {
999 push_last_chunk_with_trailers(out, trailers)?;
1000 } else {
1001 check_trailers(trailers)?;
1002 encoder.finish(out)?;
1003 }
1004 self.req = ReqWrite::Sent;
1005 self.waiting_continue = false;
1006 Ok(())
1007 }
1008
1009 /// Advances the response parser and reports what happened.
1010 ///
1011 /// # Errors
1012 ///
1013 /// Any [`HttpError`] parse error; the connection is then closed and must
1014 /// not be reused.
1015 ///
1016 /// # Examples
1017 ///
1018 /// ```
1019 /// use http_net::{ClientConn, HttpError, Request};
1020 ///
1021 /// let mut conn = ClientConn::new();
1022 /// conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new()).unwrap();
1023 /// let _ = conn.feed(b"HTTP/1.1 200 OK\r\nContent-Length: 1\r\nContent-Length: 1\r\n\r\nx");
1024 /// assert_eq!(conn.poll().unwrap_err(), HttpError::DuplicateContentLength);
1025 /// ```
1026 pub fn poll(&mut self) -> Result<ClientEvent<'_>, HttpError> {
1027 loop {
1028 match self.state {
1029 State::Idle => {
1030 if !self.buf.unread().is_empty() {
1031 return Err(self.fail(HttpError::UnexpectedData));
1032 }
1033 if self.buf.is_eof() {
1034 self.state = State::Closed;
1035 return Ok(ClientEvent::Closed);
1036 }
1037 return Ok(ClientEvent::Idle);
1038 }
1039 State::Head => return self.poll_head(),
1040 State::Interim => {
1041 self.next_head();
1042 self.state = State::Head;
1043 }
1044 State::Body => return self.poll_body(),
1045 State::Complete => {
1046 if self.reusable() && self.queued > 0 {
1047 // Pipelined: the next response is for the oldest
1048 // queued request, and may already be buffered.
1049 self.next_queued();
1050 } else if self.reusable() {
1051 self.state = State::Idle;
1052 } else {
1053 // Queued requests, if any, will get no response;
1054 // `outstanding()` says how many.
1055 self.state = State::Closed;
1056 return Ok(ClientEvent::Closed);
1057 }
1058 }
1059 State::Upgraded => return Ok(ClientEvent::Upgraded),
1060 State::Closed => return Ok(ClientEvent::Closed),
1061 }
1062 }
1063 }
1064
1065 fn poll_head(&mut self) -> Result<ClientEvent<'_>, HttpError> {
1066 loop {
1067 let start = self.buf.pin();
1068 let visible = if self.leniency.obs_fold_in_responses {
1069 let eof = self.buf.is_eof();
1070 self.unfold.run(self.buf.tail_mut(start), eof)
1071 } else {
1072 self.buf.from(start).len()
1073 };
1074 let msg = self.buf.from(start);
1075 let progress = self
1076 .scanner
1077 .advance(
1078 msg.get(..visible).unwrap_or(msg),
1079 &self.limits,
1080 HeadKind::Response,
1081 true,
1082 )
1083 .map_err(|e| self.fail(e))?;
1084 let len = match progress {
1085 Scan::Partial if self.buf.is_eof() => {
1086 return Err(self.fail(HttpError::UnexpectedEof));
1087 }
1088 Scan::Partial => return Ok(ClientEvent::NeedInput),
1089 Scan::Done(len) => len,
1090 };
1091 let info = interpret_response(
1092 self.buf.from(start),
1093 self.scanner.start_line(),
1094 &self.scanner.fields,
1095 &self.limits,
1096 self.asked,
1097 )
1098 .map_err(|e| self.fail(e))?;
1099 self.buf.consume(len);
1100 self.buf.extend_pin_to_pos();
1101 self.info = info;
1102 let status = info.status;
1103 let switched = if status == 101 {
1104 // RFC 9110 section 7.8: a 101 names the protocols it switches
1105 // to, and only to ones the request offered.
1106 let offered = self.upgrade_asked
1107 && info.upgrade_field
1108 && switches_to_offered(
1109 self.buf.from(start),
1110 &self.scanner.fields,
1111 &self.upgrade_offer,
1112 );
1113 if !offered {
1114 return Err(self.fail(HttpError::UnexpectedUpgrade));
1115 }
1116 true
1117 } else {
1118 self.asked.connect && (200..300).contains(&status)
1119 };
1120 if switched {
1121 self.waiting_continue = false;
1122 self.state = State::Upgraded;
1123 } else if (100..200).contains(&status) {
1124 // Interim response (100 Continue, 103 Early Hints): budgeted
1125 // by count and by bytes, so a server cannot keep the client
1126 // parsing forever without a final answer.
1127 self.interim_count = self.interim_count.saturating_add(1);
1128 if self.interim_count > self.limits.max_interim() {
1129 return Err(self.fail(HttpError::TooManyInterim));
1130 }
1131 self.interim_bytes = self.interim_bytes.saturating_add(len);
1132 if self.interim_bytes > self.limits.max_header_bytes() {
1133 return Err(self.fail(HttpError::HeadersTooLarge));
1134 }
1135 if status == 100 && self.waiting_continue {
1136 self.waiting_continue = false;
1137 self.next_head();
1138 return Ok(ClientEvent::Continue);
1139 }
1140 if self.report_interim {
1141 self.state = State::Interim;
1142 return Ok(ClientEvent::Informational(self.view()));
1143 }
1144 self.next_head();
1145 continue;
1146 } else {
1147 if self.waiting_continue {
1148 // RFC 9110 section 10.1.1: a final status instead of 100
1149 // means the server does not want the body. A promised
1150 // body that is never sent leaves the request unfinished,
1151 // so the connection closes after this response.
1152 self.waiting_continue = false;
1153 self.body_refused = self.req != ReqWrite::Sent;
1154 }
1155 self.decoder.start(info.framing);
1156 self.state = State::Body;
1157 }
1158 return Ok(ClientEvent::Response(self.view()));
1159 }
1160 }
1161
1162 /// Drops the current (interim) head and waits for the next one.
1163 fn next_head(&mut self) {
1164 self.buf.wipe_consumed();
1165 self.buf.unpin();
1166 self.buf.pin_at_pos();
1167 self.scanner.reset();
1168 self.unfold = Unfold::new();
1169 }
1170
1171 fn reusable(&self) -> bool {
1172 self.keep_alive && self.info.keep_alive && self.req == ReqWrite::Sent && !self.body_refused
1173 }
1174
1175 /// The response ended, the connection stays open, and nothing else is
1176 /// outstanding: only the `poll` that reports `Idle` is left, so the
1177 /// connection already answers and sends as if it had run.
1178 fn settled(&self) -> bool {
1179 self.state == State::Complete && self.queued == 0 && self.reusable()
1180 }
1181
1182 fn poll_body(&mut self) -> Result<ClientEvent<'_>, HttpError> {
1183 // A loop, not a recursive call, so the body path stays inlinable.
1184 loop {
1185 let mut framing_bytes = 0;
1186 let step = self.decoder.step(
1187 self.buf.unread(),
1188 self.buf.is_eof(),
1189 &self.limits,
1190 &mut self.trailers,
1191 self.report_trailers,
1192 &mut framing_bytes,
1193 );
1194 self.buf.consume(framing_bytes);
1195 return match step.map_err(|e| self.fail(e))? {
1196 Step::Data(n) => {
1197 let start = self.buf.pos();
1198 self.buf.consume(n);
1199 Ok(ClientEvent::Body(self.buf.range(start, start + n)))
1200 }
1201 Step::Trailers => {
1202 drop_forbidden_trailers(self.buf.unread(), &mut self.trailers.fields);
1203 if self.trailers.fields.is_empty() {
1204 // Nothing left to report: the next step ends the body.
1205 continue;
1206 }
1207 Ok(ClientEvent::Trailers(Headers::new(
1208 self.buf.unread(),
1209 &self.trailers.fields,
1210 )))
1211 }
1212 Step::Done => {
1213 self.state = State::Complete;
1214 Ok(ClientEvent::ResponseEnd)
1215 }
1216 Step::NeedMore => Ok(ClientEvent::NeedInput),
1217 };
1218 }
1219 }
1220
1221 fn fail(&mut self, e: HttpError) -> HttpError {
1222 self.response_failed = matches!(self.state, State::Head | State::Interim | State::Body);
1223 self.state = State::Closed;
1224 e
1225 }
1226
1227 fn view(&self) -> ResponseHead<'_> {
1228 ResponseHead::new(
1229 self.buf.from(self.buf.pin()),
1230 &self.scanner.fields,
1231 &self.info,
1232 )
1233 }
1234
1235 /// The current response's head, from its arrival until the connection
1236 /// moves on from it: the next `poll` after `ResponseEnd` or the next
1237 /// [`send_request`](Self::send_request) (still available after an
1238 /// upgrade).
1239 ///
1240 /// # Examples
1241 ///
1242 /// ```
1243 /// use http_net::{ClientConn, ClientEvent, Request};
1244 ///
1245 /// let mut conn = ClientConn::new();
1246 /// conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new()).unwrap();
1247 /// let _ = conn.feed(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
1248 /// while !matches!(conn.poll().unwrap(), ClientEvent::ResponseEnd) {}
1249 /// assert_eq!(conn.response().unwrap().status(), 200);
1250 /// ```
1251 #[must_use]
1252 pub fn response(&self) -> Option<ResponseHead<'_>> {
1253 match self.state {
1254 State::Body | State::Complete | State::Upgraded => Some(self.view()),
1255 _ => None,
1256 }
1257 }
1258
1259 /// Whether the server switched protocols and the connection now belongs
1260 /// to another protocol.
1261 ///
1262 /// # Examples
1263 ///
1264 /// ```
1265 /// assert!(!http_net::ClientConn::new().is_upgraded());
1266 /// ```
1267 #[must_use]
1268 pub fn is_upgraded(&self) -> bool {
1269 self.state == State::Upgraded
1270 }
1271
1272 /// Input that arrived after the last message parsed: after
1273 /// [`ClientEvent::Upgraded`], the first bytes of the new protocol.
1274 ///
1275 /// # Examples
1276 ///
1277 /// ```
1278 /// use http_net::{ClientConn, ClientEvent, Request};
1279 ///
1280 /// let mut conn = ClientConn::new();
1281 /// conn.send_request(&Request::get("/").header("host", "a").upgrade("x"), &mut Vec::new()).unwrap();
1282 /// let _ = conn.feed(b"HTTP/1.1 101 Switching Protocols\r\nConnection: upgrade\r\nUpgrade: x\r\n\r\nhi");
1283 /// assert!(matches!(conn.poll().unwrap(), ClientEvent::Response(_)));
1284 /// assert!(matches!(conn.poll().unwrap(), ClientEvent::Upgraded));
1285 /// assert_eq!(conn.remaining_input(), b"hi");
1286 /// ```
1287 #[must_use]
1288 pub fn remaining_input(&self) -> &[u8] {
1289 self.buf.unread()
1290 }
1291
1292 /// When `true`, each interim response other than `101` (for example
1293 /// `103 Early Hints`) is reported as [`ClientEvent::Informational`]
1294 /// before the final response; when `false` (the default) they are
1295 /// skipped. Interim responses count against
1296 /// [`Limits::max_interim`](crate::Limits::max_interim) either way.
1297 ///
1298 /// # Examples
1299 ///
1300 /// ```
1301 /// use http_net::{ClientConn, ClientEvent, Request};
1302 ///
1303 /// let mut conn = ClientConn::new();
1304 /// conn.set_report_informational(true);
1305 /// conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new())?;
1306 /// let _ = conn.feed(b"HTTP/1.1 103 Early Hints\r\nLink: </a.css>; rel=preload\r\n\r\nHTTP/1.1 204 No Content\r\n\r\n");
1307 /// let ClientEvent::Informational(hints) = conn.poll()? else { panic!() };
1308 /// assert_eq!(hints.status(), 103);
1309 /// assert_eq!(hints.header("link"), Some(&b"</a.css>; rel=preload"[..]));
1310 /// assert!(matches!(conn.poll()?, ClientEvent::Response(r) if r.status() == 204));
1311 /// # Ok::<(), http_net::HttpError>(())
1312 /// ```
1313 pub fn set_report_informational(&mut self, report: bool) {
1314 self.report_interim = report;
1315 }
1316
1317 /// When `true`, a response's trailer fields are kept (bounded by the
1318 /// trailer budgets of [`Limits`]) and reported as
1319 /// [`ClientEvent::Trailers`] just before [`ClientEvent::ResponseEnd`];
1320 /// when `false` (the default) they are validated and discarded.
1321 ///
1322 /// # Examples
1323 ///
1324 /// ```
1325 /// use http_net::{ClientConn, ClientEvent, Request};
1326 ///
1327 /// let mut conn = ClientConn::new();
1328 /// conn.set_report_trailers(true);
1329 /// conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new())?;
1330 /// let _ = conn.feed(b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n2\r\nok\r\n0\r\nServer-Timing: db;dur=5\r\n\r\n");
1331 /// let mut saw = false;
1332 /// loop {
1333 /// match conn.poll()? {
1334 /// ClientEvent::Trailers(t) => saw = t.len() == 1,
1335 /// ClientEvent::ResponseEnd => break,
1336 /// _ => {}
1337 /// }
1338 /// }
1339 /// assert!(saw);
1340 /// # Ok::<(), http_net::HttpError>(())
1341 /// ```
1342 pub fn set_report_trailers(&mut self, report: bool) {
1343 self.report_trailers = report;
1344 }
1345
1346 /// When `true`, the bytes of every finished response (head, body, and
1347 /// framing) are zeroed before the buffer is reused, stale copies left by
1348 /// compaction and growth are zeroed, and the whole buffer is zeroed on
1349 /// [`reset`](Self::reset), [`into_buffer`](Self::into_buffer), and drop,
1350 /// so `set-cookie` values and tokens in bodies do not linger in memory.
1351 /// Costs a `memset` of each response's bytes. Off by default; the same
1352 /// as [`ServerConn::set_wipe`](crate::ServerConn::set_wipe).
1353 ///
1354 /// # Examples
1355 ///
1356 /// ```
1357 /// let mut conn = http_net::ClientConn::new();
1358 /// conn.set_wipe(true);
1359 /// ```
1360 pub fn set_wipe(&mut self, wipe: bool) {
1361 self.buf.set_wipe(wipe);
1362 }
1363
1364 /// Sets the opt-in tolerances for responses (see [`ClientLeniency`]);
1365 /// the default is strict. Takes effect from the next response head.
1366 ///
1367 /// # Examples
1368 ///
1369 /// ```
1370 /// use http_net::{ClientConn, ClientLeniency, HttpError, Request};
1371 ///
1372 /// let folded = b"HTTP/1.1 200 OK\r\nX: a\r\n b\r\nContent-Length: 0\r\n\r\n";
1373 /// let mut strict = ClientConn::new();
1374 /// strict.send_request(&Request::get("/").header("host", "a"), &mut Vec::new())?;
1375 /// let _ = strict.feed(folded);
1376 /// assert_eq!(strict.poll().unwrap_err(), HttpError::ObsFold);
1377 ///
1378 /// let mut lenient = ClientConn::new();
1379 /// lenient.set_leniency(ClientLeniency::new().with_obs_fold_in_responses(true));
1380 /// lenient.send_request(&Request::get("/").header("host", "a"), &mut Vec::new())?;
1381 /// let _ = lenient.feed(folded);
1382 /// assert!(lenient.poll().is_ok());
1383 /// # Ok::<(), http_net::HttpError>(())
1384 /// ```
1385 pub fn set_leniency(&mut self, leniency: ClientLeniency) {
1386 self.leniency = leniency;
1387 }
1388
1389 /// Whether a request built with
1390 /// [`Request::expect_continue`](crate::Request::expect_continue) is
1391 /// still waiting: no `100 Continue`, no final response, and no body
1392 /// bytes sent yet. A client may stop waiting on its own timer and send
1393 /// the body anyway (RFC 9110 section 10.1.1).
1394 ///
1395 /// # Examples
1396 ///
1397 /// ```
1398 /// use http_net::{ClientConn, ClientEvent, Request};
1399 ///
1400 /// let mut conn = ClientConn::new();
1401 /// let mut out = Vec::new();
1402 /// let upload = Request::put("/big").header("host", "a").content_length(4).expect_continue();
1403 /// conn.send_request(&upload, &mut out)?;
1404 /// assert!(conn.is_waiting_for_continue());
1405 /// let _ = conn.feed(b"HTTP/1.1 100 Continue\r\n\r\n");
1406 /// assert!(matches!(conn.poll()?, ClientEvent::Continue));
1407 /// assert!(!conn.is_waiting_for_continue());
1408 /// conn.send_body(b"data", &mut out)?;
1409 /// conn.end_body(&mut out)?;
1410 /// # Ok::<(), http_net::HttpError>(())
1411 /// ```
1412 #[must_use]
1413 pub fn is_waiting_for_continue(&self) -> bool {
1414 self.waiting_continue
1415 }
1416
1417 /// Whether the connection can carry another request now: no request
1418 /// is outstanding (or the last response completed) and both sides allow
1419 /// keep-alive. With pipelining, [`can_send`](Self::can_send) says
1420 /// whether another request can be sent while some are outstanding.
1421 ///
1422 /// # Examples
1423 ///
1424 /// ```
1425 /// use http_net::{ClientConn, ClientEvent, Request};
1426 ///
1427 /// let mut conn = ClientConn::new();
1428 /// assert!(conn.is_reusable());
1429 /// conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new())?;
1430 /// assert!(!conn.is_reusable());
1431 /// let _ = conn.feed(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
1432 /// while !matches!(conn.poll()?, ClientEvent::ResponseEnd) {}
1433 /// assert!(conn.is_reusable());
1434 /// # Ok::<(), http_net::HttpError>(())
1435 /// ```
1436 #[must_use]
1437 pub fn is_reusable(&self) -> bool {
1438 match self.state {
1439 State::Idle => self.buf.unread().is_empty() && !self.buf.is_eof(),
1440 State::Complete => self.settled() && self.buf.unread().is_empty(),
1441 _ => false,
1442 }
1443 }
1444
1445 /// The connection's phase, for response deadlines: [`Progress::Idle`]
1446 /// with no request outstanding, [`Progress::WritingBody`] while a
1447 /// request body is streamed, [`Progress::AwaitingResponse`] until the
1448 /// first byte of the response, then [`Progress::Head`] and
1449 /// [`Progress::Body`].
1450 ///
1451 /// # Examples
1452 ///
1453 /// ```
1454 /// use http_net::{ClientConn, Progress, Request};
1455 ///
1456 /// let mut conn = ClientConn::new();
1457 /// assert_eq!(conn.progress(), Progress::Idle);
1458 /// conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new())?;
1459 /// assert_eq!(conn.progress(), Progress::AwaitingResponse);
1460 /// # Ok::<(), http_net::HttpError>(())
1461 /// ```
1462 #[must_use]
1463 pub fn progress(&self) -> Progress {
1464 if matches!(self.req, ReqWrite::Streaming(_))
1465 && !self.body_refused
1466 && !matches!(self.state, State::Closed | State::Upgraded)
1467 {
1468 return Progress::WritingBody;
1469 }
1470 match self.state {
1471 State::Idle => Progress::Idle,
1472 State::Head if self.buf.from(self.buf.pin()).is_empty() => Progress::AwaitingResponse,
1473 State::Head | State::Interim => Progress::Head,
1474 State::Body => Progress::Body,
1475 State::Complete if self.settled() => Progress::Idle,
1476 State::Complete if self.reusable() => Progress::AwaitingResponse,
1477 State::Complete | State::Closed => Progress::Closed,
1478 State::Upgraded => Progress::Upgraded,
1479 }
1480 }
1481}
1482
1483#[cfg(test)]
1484mod tests {
1485 /// With wiping on, the bytes of a finished response are zeroed when the
1486 /// next request starts, so they do not wait in the buffer for the
1487 /// connection to end (0.10).
1488 #[test]
1489 fn test_set_wipe_zeroes_a_finished_response_at_the_next_request() {
1490 use crate::{Limits, Request};
1491 for wipe in [true, false] {
1492 let mut conn = ClientConn::with_limits(Limits::new());
1493 conn.set_wipe(wipe);
1494 let get = Request::get("/").header("host", "a");
1495 conn.send_request(&get, &mut Vec::new()).unwrap();
1496 let _ = conn
1497 .feed(b"HTTP/1.1 200 OK\r\nSet-Cookie: sid=SECRET\r\nContent-Length: 2\r\n\r\nok");
1498 while !matches!(conn.poll(), Ok(ClientEvent::ResponseEnd)) {}
1499 conn.send_request(&get, &mut Vec::new()).unwrap();
1500 let held = conn.buf.range(0, 128).windows(6).any(|w| w == b"SECRET");
1501 assert_eq!(held, !wipe, "wipe {wipe}");
1502 }
1503 }
1504
1505 use super::*;
1506 use crate::head::BodyFraming;
1507
1508 fn get() -> Request {
1509 Request::get("/").header("host", "a")
1510 }
1511
1512 #[test]
1513 fn test_interim_responses_are_skipped() {
1514 let mut conn = ClientConn::new();
1515 conn.send_request(&get(), &mut Vec::new()).unwrap();
1516 let _ = conn.feed(b"HTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 103 Early Hints\r\nLink: </a>\r\n\r\nHTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nok");
1517 let ClientEvent::Response(r) = conn.poll().unwrap() else {
1518 panic!("expected response");
1519 };
1520 assert_eq!(r.status(), 200);
1521 assert!(matches!(conn.poll().unwrap(), ClientEvent::Body(b"ok")));
1522 assert!(matches!(conn.poll().unwrap(), ClientEvent::ResponseEnd));
1523 assert!(matches!(conn.poll().unwrap(), ClientEvent::Idle));
1524 }
1525
1526 #[test]
1527 fn test_unrequested_upgrade_and_unsolicited_data_are_errors() {
1528 let mut conn = ClientConn::new();
1529 conn.send_request(&get(), &mut Vec::new()).unwrap();
1530 let _ = conn
1531 .feed(b"HTTP/1.1 101 Switching Protocols\r\nUpgrade: x\r\nConnection: upgrade\r\n\r\n");
1532 assert_eq!(conn.poll().unwrap_err(), HttpError::UnexpectedUpgrade);
1533
1534 let mut conn = ClientConn::new();
1535 let _ = conn.feed(b"HTTP/1.1 200 OK\r\n\r\n");
1536 assert_eq!(conn.poll().unwrap_err(), HttpError::UnexpectedData);
1537 }
1538
1539 /// T-21: a `101` switches only to a protocol the request offered (RFC
1540 /// 9110 section 7.8); a back end answering an upgrade to something else
1541 /// must not take over the connection.
1542 #[test]
1543 fn test_switching_protocols_must_name_an_offered_protocol() {
1544 let answer = |offers: &[&str], upgrade: &str| {
1545 let mut request = Request::get("/").header("host", "a");
1546 for offer in offers {
1547 request = request.upgrade(offer);
1548 }
1549 let mut conn = ClientConn::new();
1550 conn.send_request(&request, &mut Vec::new()).unwrap();
1551 let response = alloc::format!(
1552 "HTTP/1.1 101 Switching Protocols\r\nConnection: upgrade\r\n{upgrade}\r\n"
1553 );
1554 let _ = conn.feed(response.as_bytes());
1555 match conn.poll() {
1556 Ok(ClientEvent::Response(r)) => {
1557 assert_eq!(r.status(), 101);
1558 matches!(conn.poll(), Ok(ClientEvent::Upgraded))
1559 }
1560 Err(e) => {
1561 assert_eq!(e, HttpError::UnexpectedUpgrade);
1562 false
1563 }
1564 other => panic!("unexpected {other:?}"),
1565 }
1566 };
1567 // The protocol offered, in any case, with or without a version.
1568 assert!(answer(&["websocket"], "Upgrade: websocket\r\n"));
1569 assert!(answer(&["websocket"], "Upgrade: WebSocket\r\n"));
1570 assert!(answer(&["websocket/13"], "Upgrade: websocket\r\n"));
1571 assert!(answer(&["websocket"], "Upgrade: websocket/13\r\n"));
1572 // One of several offers.
1573 assert!(answer(&["foo", "websocket"], "Upgrade: websocket\r\n"));
1574 // Anything not offered, h2c above all, or no protocol at all.
1575 assert!(!answer(&["websocket"], "Upgrade: h2c\r\n"));
1576 assert!(!answer(&["websocket"], "Upgrade: foo\r\n"));
1577 assert!(!answer(&["websocket"], "Upgrade: websocket, foo\r\n"));
1578 assert!(!answer(
1579 &["websocket"],
1580 "Upgrade: websocket\r\nUpgrade: foo\r\n"
1581 ));
1582 assert!(!answer(&["websocket/13"], "Upgrade: websocket/14\r\n"));
1583 assert!(!answer(&["websocket"], "Upgrade:\r\n"));
1584 assert!(!answer(&["websocket"], ""));
1585 }
1586
1587 #[test]
1588 fn test_head_response_has_no_body_and_connection_is_reused() {
1589 let mut conn = ClientConn::new();
1590 conn.send_request(
1591 &Request::new("HEAD", "/").header("host", "a"),
1592 &mut Vec::new(),
1593 )
1594 .unwrap();
1595 let _ = conn.feed(b"HTTP/1.1 200 OK\r\nContent-Length: 100\r\n\r\n");
1596 let ClientEvent::Response(r) = conn.poll().unwrap() else {
1597 panic!("expected response");
1598 };
1599 assert_eq!(r.framing(), BodyFraming::None);
1600 assert!(matches!(conn.poll().unwrap(), ClientEvent::ResponseEnd));
1601 assert!(matches!(conn.poll().unwrap(), ClientEvent::Idle));
1602 conn.send_request(&get(), &mut Vec::new()).unwrap();
1603 assert!(matches!(
1604 conn.send_request(&get(), &mut Vec::new()),
1605 Err(HttpError::InvalidState(_))
1606 ));
1607 }
1608
1609 #[test]
1610 fn test_close_and_streaming_request_prevent_reuse() {
1611 let mut conn = ClientConn::new();
1612 conn.send_request(&get().close(), &mut Vec::new()).unwrap();
1613 let _ = conn.feed(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
1614 while !matches!(conn.poll().unwrap(), ClientEvent::ResponseEnd) {}
1615 assert!(matches!(conn.poll().unwrap(), ClientEvent::Closed));
1616 assert!(conn.send_request(&get(), &mut Vec::new()).is_err());
1617
1618 let mut conn = ClientConn::new();
1619 let mut out = Vec::new();
1620 conn.send_request(&Request::post("/").header("host", "a").chunked(), &mut out)
1621 .unwrap();
1622 let _ = conn.feed(b"HTTP/1.1 413 Content Too Large\r\nContent-Length: 0\r\n\r\n");
1623 while !matches!(conn.poll().unwrap(), ClientEvent::ResponseEnd) {}
1624 assert!(matches!(conn.poll().unwrap(), ClientEvent::Closed));
1625 assert!(matches!(conn.send_body(b"x", &mut out), Ok(())));
1626 assert!(matches!(conn.end_body(&mut out), Ok(())));
1627 assert!(matches!(
1628 conn.end_body(&mut out),
1629 Err(HttpError::InvalidState(_))
1630 ));
1631 }
1632
1633 #[test]
1634 fn test_eof_before_response_is_an_error() {
1635 let mut conn = ClientConn::new();
1636 conn.send_request(&get(), &mut Vec::new()).unwrap();
1637 conn.feed_eof();
1638 assert_eq!(conn.poll().unwrap_err(), HttpError::UnexpectedEof);
1639 }
1640}