Skip to main content

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, Progress, ResponseHead, ResponseInfo, interpret_response,
11    },
12    limits::Limits,
13    message::{OutBody, Request},
14    method::Method,
15};
16
17/// What a [`ClientConn`] has for the caller after [`poll`](ClientConn::poll).
18///
19/// # Examples
20///
21/// ```
22/// use http_net::{ClientConn, ClientEvent, Request};
23///
24/// let mut conn = ClientConn::new();
25/// let mut out = Vec::new();
26/// conn.send_request(&Request::get("/").header("host", "example.com"), &mut out).unwrap();
27/// 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");
28/// let mut body = Vec::new();
29/// loop {
30///     match conn.poll().unwrap() {
31///         ClientEvent::Response(resp) => assert_eq!(resp.status(), 200),
32///         ClientEvent::Body(chunk) => body.extend_from_slice(chunk),
33///         ClientEvent::ResponseEnd => break,
34///         other => panic!("{other:?}"),
35///     }
36/// }
37/// assert_eq!(body, b"abc");
38/// ```
39#[non_exhaustive]
40#[derive(Debug)]
41pub enum ClientEvent<'a> {
42    /// The response head arrived (interim 1xx responses other than 101 are
43    /// skipped; together they count against
44    /// [`Limits::max_header_bytes`](crate::Limits::max_header_bytes)). Its
45    /// body follows as [`Body`](ClientEvent::Body) events, then
46    /// [`ResponseEnd`](ClientEvent::ResponseEnd).
47    Response(ResponseHead<'a>),
48    /// A run of body bytes, after chunked decoding.
49    Body(&'a [u8]),
50    /// The response is complete.
51    ResponseEnd,
52    /// More input is needed (or, for a body that runs until close,
53    /// [`feed_eof`](ClientConn::feed_eof) once the server closes).
54    NeedInput,
55    /// No request is outstanding; send the next one.
56    Idle,
57    /// The server switched protocols (101 to an upgrade request, or 2xx to
58    /// `CONNECT`); the response head was reported just before. Bytes of the
59    /// new protocol that already arrived are in
60    /// [`remaining_input`](ClientConn::remaining_input).
61    Upgraded,
62    /// The connection cannot carry another request: close the socket.
63    Closed,
64}
65
66#[derive(Clone, Copy, Debug, PartialEq, Eq)]
67enum State {
68    Idle,
69    Head,
70    Body,
71    Complete,
72    Upgraded,
73    Closed,
74}
75
76#[derive(Clone, Copy, Debug, PartialEq, Eq)]
77enum ReqWrite {
78    Streaming(Encoder),
79    Sent,
80}
81
82/// The client side of one HTTP/1.1 connection: a sans-IO state machine.
83///
84/// One request is outstanding at a time. [`send_request`](Self::send_request)
85/// serializes a [`Request`]; [`feed`](Self::feed) and [`poll`](Self::poll)
86/// parse the response. When the response allows it, the connection returns
87/// to [`ClientEvent::Idle`] and can send the next request (keep-alive).
88///
89/// The response is framed with the request in mind: a `HEAD` request's
90/// response has no body whatever its headers say, a `CONNECT` 2xx becomes a
91/// tunnel, and a response with neither `Content-Length` nor
92/// `Transfer-Encoding` runs until the server closes.
93///
94/// # Examples
95///
96/// ```
97/// use http_net::{ClientConn, ClientEvent, Request};
98///
99/// let mut conn = ClientConn::new();
100/// let mut out = Vec::new();
101/// conn.send_request(&Request::get("/a").header("host", "h"), &mut out).unwrap();
102/// assert_eq!(out, b"GET /a HTTP/1.1\r\nhost: h\r\n\r\n");
103/// let _ = conn.feed(b"HTTP/1.1 204 No Content\r\n\r\n");
104/// assert!(matches!(conn.poll().unwrap(), ClientEvent::Response(_)));
105/// assert!(matches!(conn.poll().unwrap(), ClientEvent::ResponseEnd));
106/// assert!(matches!(conn.poll().unwrap(), ClientEvent::Idle));
107/// ```
108pub struct ClientConn {
109    buf: Buffer,
110    limits: Limits,
111    scanner: HeadScanner,
112    trailers: HeadScanner,
113    decoder: Decoder,
114    info: ResponseInfo,
115    asked: Asked,
116    upgrade_asked: bool,
117    state: State,
118    req: ReqWrite,
119    keep_alive: bool,
120    /// Bytes of interim (1xx) heads skipped for the current request. They
121    /// count against the header budget so that a server cannot keep the
122    /// client parsing forever without answering.
123    interim_bytes: usize,
124}
125
126impl Default for ClientConn {
127    fn default() -> Self {
128        Self::new()
129    }
130}
131
132impl core::fmt::Debug for ClientConn {
133    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
134        f.debug_struct("ClientConn")
135            .field("state", &self.state)
136            .field("request", &self.req)
137            .field("buffered", &self.buf.unread().len())
138            .field("limits", &self.limits)
139            .finish()
140    }
141}
142
143impl ClientConn {
144    /// A connection with the default [`Limits`].
145    ///
146    /// # Examples
147    ///
148    /// ```
149    /// let mut conn = http_net::ClientConn::new();
150    /// assert!(matches!(conn.poll(), Ok(http_net::ClientEvent::Idle)));
151    /// ```
152    #[must_use]
153    pub fn new() -> Self {
154        Self::with_limits(Limits::new())
155    }
156
157    /// A connection enforcing `limits` on responses.
158    ///
159    /// # Examples
160    ///
161    /// ```
162    /// let conn = http_net::ClientConn::with_limits(http_net::Limits::new().with_max_body(1 << 24));
163    /// assert_eq!(conn.limits().max_body(), 1 << 24);
164    /// ```
165    #[must_use]
166    pub fn with_limits(limits: Limits) -> Self {
167        Self {
168            buf: Buffer::new(limits.buffer_capacity()),
169            limits,
170            scanner: HeadScanner::new(),
171            trailers: HeadScanner::new(),
172            decoder: Decoder::new(),
173            info: ResponseInfo::EMPTY,
174            asked: Asked {
175                head: false,
176                connect: false,
177            },
178            upgrade_asked: false,
179            state: State::Idle,
180            req: ReqWrite::Sent,
181            keep_alive: true,
182            interim_bytes: 0,
183        }
184    }
185
186    /// The limits this connection enforces.
187    ///
188    /// # Examples
189    ///
190    /// ```
191    /// assert_eq!(http_net::ClientConn::new().limits().max_headers(), 100);
192    /// ```
193    #[must_use]
194    pub fn limits(&self) -> &Limits {
195        &self.limits
196    }
197
198    /// Copies as much of `bytes` into the input buffer as fits; returns how
199    /// many were taken (see [`ServerConn::feed`](crate::ServerConn::feed)).
200    ///
201    /// # Examples
202    ///
203    /// ```
204    /// let mut conn = http_net::ClientConn::new();
205    /// assert_eq!(conn.feed(b"HTTP/1.1"), 8);
206    /// ```
207    #[must_use = "bytes beyond the returned count were not taken"]
208    pub fn feed(&mut self, bytes: &[u8]) -> usize {
209        self.buf.feed(bytes)
210    }
211
212    /// Spare space at the end of the input buffer; follow with
213    /// [`commit`](Self::commit).
214    ///
215    /// # Examples
216    ///
217    /// ```
218    /// let mut conn = http_net::ClientConn::new();
219    /// assert!(!conn.read_buf().is_empty());
220    /// ```
221    pub fn read_buf(&mut self) -> &mut [u8] {
222        self.buf.spare()
223    }
224
225    /// Records that `n` bytes were written into [`read_buf`](Self::read_buf).
226    ///
227    /// # Examples
228    ///
229    /// ```
230    /// let mut conn = http_net::ClientConn::new();
231    /// conn.read_buf()[..4].copy_from_slice(b"HTTP");
232    /// conn.commit(4);
233    /// ```
234    pub fn commit(&mut self, n: usize) {
235        self.buf.commit(n);
236    }
237
238    /// Records that the server closed its sending side: ends a body that
239    /// runs until close, or reports [`HttpError::UnexpectedEof`] mid-message.
240    ///
241    /// # Examples
242    ///
243    /// ```
244    /// use http_net::{ClientConn, ClientEvent, Request};
245    ///
246    /// let mut conn = ClientConn::new();
247    /// conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new()).unwrap();
248    /// let _ = conn.feed(b"HTTP/1.0 200 OK\r\n\r\nall of it");
249    /// assert!(matches!(conn.poll().unwrap(), ClientEvent::Response(_)));
250    /// assert!(matches!(conn.poll().unwrap(), ClientEvent::Body(b"all of it")));
251    /// assert!(matches!(conn.poll().unwrap(), ClientEvent::NeedInput));
252    /// conn.feed_eof();
253    /// assert!(matches!(conn.poll().unwrap(), ClientEvent::ResponseEnd));
254    /// assert!(matches!(conn.poll().unwrap(), ClientEvent::Closed));
255    /// ```
256    pub fn feed_eof(&mut self) {
257        self.buf.set_eof();
258    }
259
260    /// Writes `request` into `out` and starts waiting for its response.
261    ///
262    /// A full body is sent with `content-length` (omitted for an empty body
263    /// on methods that do not expect one); a streamed body continues with
264    /// [`send_body`](Self::send_body) and [`end_body`](Self::end_body).
265    ///
266    /// # Errors
267    ///
268    /// - the first error recorded by the [`Request`] builder;
269    /// - [`HttpError::MissingHost`] when the request has no `host` header;
270    /// - [`HttpError::InvalidState`] when a request is already outstanding
271    ///   or the connection is closed.
272    ///
273    /// # Examples
274    ///
275    /// ```
276    /// use http_net::{ClientConn, HttpError, Request};
277    ///
278    /// let mut conn = ClientConn::new();
279    /// let mut out = Vec::new();
280    /// assert_eq!(conn.send_request(&Request::get("/"), &mut out), Err(HttpError::MissingHost));
281    /// conn.send_request(&Request::post("/f").header("host", "a").body(&b"xyz"[..]), &mut out).unwrap();
282    /// assert_eq!(out, b"POST /f HTTP/1.1\r\nhost: a\r\ncontent-length: 3\r\n\r\nxyz");
283    /// ```
284    pub fn send_request(&mut self, request: &Request, out: &mut Vec<u8>) -> Result<(), HttpError> {
285        if let Some(e) = request.error() {
286            return Err(e);
287        }
288        match self.state {
289            State::Idle => {}
290            State::Closed | State::Upgraded => {
291                return Err(HttpError::InvalidState(
292                    "the connection is closed or upgraded",
293                ));
294            }
295            _ => return Err(HttpError::InvalidState("a request is already outstanding")),
296        }
297        if !self.buf.unread().is_empty() {
298            return Err(HttpError::UnexpectedData);
299        }
300        if !request.has_host {
301            return Err(HttpError::MissingHost);
302        }
303        let method = request.method_kind;
304        let expects_body = matches!(method, Method::Post | Method::Put | Method::Patch);
305        let (encoder, declared) = match &request.body {
306            OutBody::Full(b) if b.is_empty() && !expects_body => (Encoder::Discard, None),
307            OutBody::Full(b) => (
308                Encoder::Length {
309                    remaining: b.len() as u64,
310                },
311                Some(b.len() as u64),
312            ),
313            OutBody::Length(n) => (Encoder::Length { remaining: *n }, Some(*n)),
314            OutBody::Chunked => (Encoder::Chunked, None),
315        };
316        out.extend_from_slice(request.method().as_bytes());
317        out.push(b' ');
318        out.extend_from_slice(request.target().as_bytes());
319        out.extend_from_slice(b" HTTP/1.1\r\n");
320        out.extend_from_slice(request.fields());
321        push_framing(out, encoder, declared);
322        if request.close {
323            out.extend_from_slice(b"connection: close\r\n");
324        }
325        out.extend_from_slice(b"\r\n");
326        let mut encoder = encoder;
327        match &request.body {
328            OutBody::Full(b) => {
329                encoder.write(b, out)?;
330                self.req = ReqWrite::Sent;
331            }
332            _ => self.req = ReqWrite::Streaming(encoder),
333        }
334        self.asked = Asked {
335            head: method == Method::Head,
336            connect: method == Method::Connect,
337        };
338        self.upgrade_asked = request.upgrade;
339        self.keep_alive = !request.close;
340        self.buf.unpin();
341        self.buf.pin_at_pos();
342        self.scanner.reset();
343        self.interim_bytes = 0;
344        self.state = State::Head;
345        Ok(())
346    }
347
348    /// Writes body bytes of a streamed request.
349    ///
350    /// # Errors
351    ///
352    /// [`HttpError::BodyLengthMismatch`] past the declared length;
353    /// [`HttpError::InvalidState`] when no streamed request is in progress.
354    ///
355    /// # Examples
356    ///
357    /// ```
358    /// use http_net::{ClientConn, Request};
359    ///
360    /// let mut conn = ClientConn::new();
361    /// let mut out = Vec::new();
362    /// conn.send_request(&Request::post("/up").header("host", "a").chunked(), &mut out).unwrap();
363    /// conn.send_body(b"part", &mut out).unwrap();
364    /// conn.end_body(&mut out).unwrap();
365    /// assert!(out.ends_with(b"4\r\npart\r\n0\r\n\r\n"));
366    /// ```
367    pub fn send_body(&mut self, data: &[u8], out: &mut Vec<u8>) -> Result<(), HttpError> {
368        match &mut self.req {
369            ReqWrite::Streaming(encoder) => encoder.write(data, out),
370            ReqWrite::Sent => Err(HttpError::InvalidState("no streamed request in progress")),
371        }
372    }
373
374    /// Ends a streamed request body.
375    ///
376    /// # Errors
377    ///
378    /// [`HttpError::BodyLengthMismatch`] when fewer bytes than declared were
379    /// sent; [`HttpError::InvalidState`] when no streamed request is in
380    /// progress.
381    ///
382    /// # Examples
383    ///
384    /// ```
385    /// use http_net::{ClientConn, Request};
386    ///
387    /// let mut conn = ClientConn::new();
388    /// let mut out = Vec::new();
389    /// conn.send_request(&Request::post("/").header("host", "a").content_length(2), &mut out).unwrap();
390    /// conn.send_body(b"ok", &mut out).unwrap();
391    /// conn.end_body(&mut out).unwrap();
392    /// assert!(out.ends_with(b"\r\n\r\nok"));
393    /// ```
394    pub fn end_body(&mut self, out: &mut Vec<u8>) -> Result<(), HttpError> {
395        match &mut self.req {
396            ReqWrite::Streaming(encoder) => {
397                encoder.finish(out)?;
398                self.req = ReqWrite::Sent;
399                Ok(())
400            }
401            ReqWrite::Sent => Err(HttpError::InvalidState("no streamed request in progress")),
402        }
403    }
404
405    /// Advances the response parser and reports what happened.
406    ///
407    /// # Errors
408    ///
409    /// Any [`HttpError`] parse error; the connection is then closed and must
410    /// not be reused.
411    ///
412    /// # Examples
413    ///
414    /// ```
415    /// use http_net::{ClientConn, HttpError, Request};
416    ///
417    /// let mut conn = ClientConn::new();
418    /// conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new()).unwrap();
419    /// let _ = conn.feed(b"HTTP/1.1 200 OK\r\nContent-Length: 1\r\nContent-Length: 1\r\n\r\nx");
420    /// assert_eq!(conn.poll().unwrap_err(), HttpError::DuplicateContentLength);
421    /// ```
422    pub fn poll(&mut self) -> Result<ClientEvent<'_>, HttpError> {
423        loop {
424            match self.state {
425                State::Idle => {
426                    if !self.buf.unread().is_empty() {
427                        return Err(self.fail(HttpError::UnexpectedData));
428                    }
429                    if self.buf.is_eof() {
430                        self.state = State::Closed;
431                        return Ok(ClientEvent::Closed);
432                    }
433                    return Ok(ClientEvent::Idle);
434                }
435                State::Head => return self.poll_head(),
436                State::Body => return self.poll_body(),
437                State::Complete => {
438                    let reusable =
439                        self.keep_alive && self.info.keep_alive && self.req == ReqWrite::Sent;
440                    if reusable {
441                        self.state = State::Idle;
442                    } else {
443                        self.state = State::Closed;
444                        return Ok(ClientEvent::Closed);
445                    }
446                }
447                State::Upgraded => return Ok(ClientEvent::Upgraded),
448                State::Closed => return Ok(ClientEvent::Closed),
449            }
450        }
451    }
452
453    fn poll_head(&mut self) -> Result<ClientEvent<'_>, HttpError> {
454        loop {
455            let start = self.buf.pin();
456            let progress = self
457                .scanner
458                .advance(self.buf.from(start), &self.limits, HeadKind::Response, true)
459                .map_err(|e| self.fail(e))?;
460            let len = match progress {
461                Progress::Partial if self.buf.is_eof() => {
462                    return Err(self.fail(HttpError::UnexpectedEof));
463                }
464                Progress::Partial => return Ok(ClientEvent::NeedInput),
465                Progress::Done(len) => len,
466            };
467            let info = interpret_response(
468                self.buf.from(start),
469                self.scanner.start_line(),
470                &self.scanner.fields,
471                &self.limits,
472                self.asked,
473            )
474            .map_err(|e| self.fail(e))?;
475            self.buf.consume(len);
476            self.buf.extend_pin_to_pos();
477            self.info = info;
478            let status = info.status;
479            let switched = if status == 101 {
480                if !self.upgrade_asked || !info.upgrade_field {
481                    return Err(self.fail(HttpError::UnexpectedUpgrade));
482                }
483                true
484            } else {
485                self.asked.connect && (200..300).contains(&status)
486            };
487            if switched {
488                self.state = State::Upgraded;
489            } else if (100..200).contains(&status) {
490                // Interim response (100 Continue, 103 Early Hints): skip it
491                // and keep waiting for the final one.
492                self.interim_bytes = self.interim_bytes.saturating_add(len);
493                if self.interim_bytes > self.limits.max_header_bytes() {
494                    return Err(self.fail(HttpError::HeadersTooLarge));
495                }
496                self.buf.unpin();
497                self.buf.pin_at_pos();
498                self.scanner.reset();
499                continue;
500            } else {
501                self.decoder.start(info.framing);
502                self.state = State::Body;
503            }
504            return Ok(ClientEvent::Response(self.view()));
505        }
506    }
507
508    fn poll_body(&mut self) -> Result<ClientEvent<'_>, HttpError> {
509        let mut framing_bytes = 0;
510        let step = self.decoder.step(
511            self.buf.unread(),
512            self.buf.is_eof(),
513            &self.limits,
514            &mut self.trailers,
515            &mut framing_bytes,
516        );
517        self.buf.consume(framing_bytes);
518        match step.map_err(|e| self.fail(e))? {
519            Step::Data(n) => {
520                let start = self.buf.pos();
521                self.buf.consume(n);
522                Ok(ClientEvent::Body(self.buf.range(start, start + n)))
523            }
524            Step::Done => {
525                self.state = State::Complete;
526                Ok(ClientEvent::ResponseEnd)
527            }
528            Step::NeedMore => Ok(ClientEvent::NeedInput),
529        }
530    }
531
532    fn fail(&mut self, e: HttpError) -> HttpError {
533        self.state = State::Closed;
534        e
535    }
536
537    fn view(&self) -> ResponseHead<'_> {
538        ResponseHead::new(
539            self.buf.from(self.buf.pin()),
540            &self.scanner.fields,
541            &self.info,
542        )
543    }
544
545    /// The current response's head, from its arrival until the next request
546    /// is sent (still available after an upgrade).
547    ///
548    /// # Examples
549    ///
550    /// ```
551    /// use http_net::{ClientConn, ClientEvent, Request};
552    ///
553    /// let mut conn = ClientConn::new();
554    /// conn.send_request(&Request::get("/").header("host", "a"), &mut Vec::new()).unwrap();
555    /// let _ = conn.feed(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
556    /// while !matches!(conn.poll().unwrap(), ClientEvent::ResponseEnd) {}
557    /// assert_eq!(conn.response().unwrap().status(), 200);
558    /// ```
559    #[must_use]
560    pub fn response(&self) -> Option<ResponseHead<'_>> {
561        match self.state {
562            State::Body | State::Complete | State::Upgraded => Some(self.view()),
563            _ => None,
564        }
565    }
566
567    /// Whether the server switched protocols and the connection now belongs
568    /// to another protocol.
569    ///
570    /// # Examples
571    ///
572    /// ```
573    /// assert!(!http_net::ClientConn::new().is_upgraded());
574    /// ```
575    #[must_use]
576    pub fn is_upgraded(&self) -> bool {
577        self.state == State::Upgraded
578    }
579
580    /// Input that arrived after the last message parsed: after
581    /// [`ClientEvent::Upgraded`], the first bytes of the new protocol.
582    ///
583    /// # Examples
584    ///
585    /// ```
586    /// use http_net::{ClientConn, ClientEvent, Request};
587    ///
588    /// let mut conn = ClientConn::new();
589    /// conn.send_request(&Request::get("/").header("host", "a").upgrade("x"), &mut Vec::new()).unwrap();
590    /// let _ = conn.feed(b"HTTP/1.1 101 Switching Protocols\r\nConnection: upgrade\r\nUpgrade: x\r\n\r\nhi");
591    /// assert!(matches!(conn.poll().unwrap(), ClientEvent::Response(_)));
592    /// assert!(matches!(conn.poll().unwrap(), ClientEvent::Upgraded));
593    /// assert_eq!(conn.remaining_input(), b"hi");
594    /// ```
595    #[must_use]
596    pub fn remaining_input(&self) -> &[u8] {
597        self.buf.unread()
598    }
599}
600
601#[cfg(test)]
602mod tests {
603    use super::*;
604    use crate::head::BodyFraming;
605
606    fn get() -> Request {
607        Request::get("/").header("host", "a")
608    }
609
610    #[test]
611    fn test_interim_responses_are_skipped() {
612        let mut conn = ClientConn::new();
613        conn.send_request(&get(), &mut Vec::new()).unwrap();
614        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");
615        let ClientEvent::Response(r) = conn.poll().unwrap() else {
616            panic!("expected response");
617        };
618        assert_eq!(r.status(), 200);
619        assert!(matches!(conn.poll().unwrap(), ClientEvent::Body(b"ok")));
620        assert!(matches!(conn.poll().unwrap(), ClientEvent::ResponseEnd));
621        assert!(matches!(conn.poll().unwrap(), ClientEvent::Idle));
622    }
623
624    #[test]
625    fn test_unrequested_upgrade_and_unsolicited_data_are_errors() {
626        let mut conn = ClientConn::new();
627        conn.send_request(&get(), &mut Vec::new()).unwrap();
628        let _ = conn
629            .feed(b"HTTP/1.1 101 Switching Protocols\r\nUpgrade: x\r\nConnection: upgrade\r\n\r\n");
630        assert_eq!(conn.poll().unwrap_err(), HttpError::UnexpectedUpgrade);
631
632        let mut conn = ClientConn::new();
633        let _ = conn.feed(b"HTTP/1.1 200 OK\r\n\r\n");
634        assert_eq!(conn.poll().unwrap_err(), HttpError::UnexpectedData);
635    }
636
637    #[test]
638    fn test_head_response_has_no_body_and_connection_is_reused() {
639        let mut conn = ClientConn::new();
640        conn.send_request(
641            &Request::new("HEAD", "/").header("host", "a"),
642            &mut Vec::new(),
643        )
644        .unwrap();
645        let _ = conn.feed(b"HTTP/1.1 200 OK\r\nContent-Length: 100\r\n\r\n");
646        let ClientEvent::Response(r) = conn.poll().unwrap() else {
647            panic!("expected response");
648        };
649        assert_eq!(r.framing(), BodyFraming::None);
650        assert!(matches!(conn.poll().unwrap(), ClientEvent::ResponseEnd));
651        assert!(matches!(conn.poll().unwrap(), ClientEvent::Idle));
652        conn.send_request(&get(), &mut Vec::new()).unwrap();
653        assert!(matches!(
654            conn.send_request(&get(), &mut Vec::new()),
655            Err(HttpError::InvalidState(_))
656        ));
657    }
658
659    #[test]
660    fn test_close_and_streaming_request_prevent_reuse() {
661        let mut conn = ClientConn::new();
662        conn.send_request(&get().close(), &mut Vec::new()).unwrap();
663        let _ = conn.feed(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
664        while !matches!(conn.poll().unwrap(), ClientEvent::ResponseEnd) {}
665        assert!(matches!(conn.poll().unwrap(), ClientEvent::Closed));
666        assert!(conn.send_request(&get(), &mut Vec::new()).is_err());
667
668        let mut conn = ClientConn::new();
669        let mut out = Vec::new();
670        conn.send_request(&Request::post("/").header("host", "a").chunked(), &mut out)
671            .unwrap();
672        let _ = conn.feed(b"HTTP/1.1 413 Content Too Large\r\nContent-Length: 0\r\n\r\n");
673        while !matches!(conn.poll().unwrap(), ClientEvent::ResponseEnd) {}
674        assert!(matches!(conn.poll().unwrap(), ClientEvent::Closed));
675        assert!(matches!(conn.send_body(b"x", &mut out), Ok(())));
676        assert!(matches!(conn.end_body(&mut out), Ok(())));
677        assert!(matches!(
678            conn.end_body(&mut out),
679            Err(HttpError::InvalidState(_))
680        ));
681    }
682
683    #[test]
684    fn test_eof_before_response_is_an_error() {
685        let mut conn = ClientConn::new();
686        conn.send_request(&get(), &mut Vec::new()).unwrap();
687        conn.feed_eof();
688        assert_eq!(conn.poll().unwrap_err(), HttpError::UnexpectedEof);
689    }
690}