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}