1use std::{
2 error::Error as StdError,
3 future::Future,
4 marker::Unpin,
5 pin::Pin,
6 task::{Context, Poll},
7};
8
9use crate::rt::{Read, Write};
10use bytes::{Buf, Bytes};
11use futures_core::ready;
12use http::Request;
13
14use super::{Http1Transaction, Wants};
15use crate::body::{Body, DecodedLength, Incoming as IncomingBody};
16#[cfg(feature = "client")]
17use crate::client::dispatch::TrySendError;
18use crate::common::task;
19use crate::proto::{BodyLength, Conn, Dispatched, MessageHead, RequestHead};
20use crate::upgrade::OnUpgrade;
21
22pub(crate) struct Dispatcher<D, Bs: Body, I, T> {
23 conn: Conn<I, Bs::Data, T>,
24 dispatch: D,
25 body_tx: SenderDropGuard,
26 body_rx: Pin<Box<Option<Bs>>>,
27 is_closing: bool,
28}
29
30pub(crate) trait Dispatch {
31 type PollItem;
32 type PollBody;
33 type PollError;
34 type RecvItem;
35 fn poll_msg(
36 self: Pin<&mut Self>,
37 cx: &mut Context<'_>,
38 ) -> Poll<Option<Result<(Self::PollItem, Self::PollBody), Self::PollError>>>;
39 fn recv_msg(&mut self, msg: crate::Result<(Self::RecvItem, IncomingBody)>)
40 -> crate::Result<()>;
41 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), ()>>;
42 fn should_poll(&self) -> bool;
43}
44
45cfg_server! {
46 use crate::service::HttpService;
47
48 pub(crate) struct Server<S: HttpService<B>, B> {
49 in_flight: Pin<Box<Option<S::Future>>>,
50 pub(crate) service: S,
51 }
52}
53
54cfg_client! {
55 pin_project_lite::pin_project! {
56 pub(crate) struct Client<B> {
57 callback: Option<crate::client::dispatch::Callback<Request<B>, http::Response<IncomingBody>>>,
58 #[pin]
59 rx: ClientRx<B>,
60 rx_closed: bool,
61 }
62 }
63
64 type ClientRx<B> = crate::client::dispatch::Receiver<Request<B>, http::Response<IncomingBody>>;
65}
66
67impl<D, Bs, I, T> Dispatcher<D, Bs, I, T>
68where
69 D: Dispatch<
70 PollItem = MessageHead<T::Outgoing>,
71 PollBody = Bs,
72 RecvItem = MessageHead<T::Incoming>,
73 > + Unpin,
74 D::PollError: Into<Box<dyn StdError + Send + Sync>>,
75 I: Read + Write + Unpin,
76 T: Http1Transaction + Unpin,
77 Bs: Body + 'static,
78 Bs::Error: Into<Box<dyn StdError + Send + Sync>>,
79{
80 pub(crate) fn new(dispatch: D, conn: Conn<I, Bs::Data, T>) -> Self {
81 Dispatcher {
82 conn,
83 dispatch,
84 body_tx: SenderDropGuard::none(),
85 body_rx: Box::pin(None),
86 is_closing: false,
87 }
88 }
89
90 #[cfg(feature = "server")]
91 pub(crate) fn disable_keep_alive(&mut self) {
92 self.conn.disable_keep_alive();
93
94 if self.conn.is_write_closed() || self.conn.has_initial_read_write_state() {
98 self.close();
99 }
100 }
101
102 pub(crate) fn into_inner(self) -> (I, Bytes, D) {
103 let (io, buf) = self.conn.into_inner();
104 (io, buf, self.dispatch)
105 }
106
107 pub(crate) fn poll_without_shutdown(
113 &mut self,
114 cx: &mut Context<'_>,
115 ) -> Poll<crate::Result<()>> {
116 Pin::new(self).poll_catch(cx, false).map_ok(|ds| {
117 if let Dispatched::Upgrade(pending) = ds {
118 pending.manual();
119 }
120 })
121 }
122
123 fn poll_catch(
124 &mut self,
125 cx: &mut Context<'_>,
126 should_shutdown: bool,
127 ) -> Poll<crate::Result<Dispatched>> {
128 Poll::Ready(ready!(self.poll_inner(cx, should_shutdown)).or_else(|e| {
129 if let Some(mut body) = self.body_tx.take() {
132 body.send_error(crate::Error::new_body("connection error"));
133 }
134 self.dispatch.recv_msg(Err(e))?;
139 Ok(Dispatched::Shutdown)
140 }))
141 }
142
143 fn poll_inner(
144 &mut self,
145 cx: &mut Context<'_>,
146 should_shutdown: bool,
147 ) -> Poll<crate::Result<Dispatched>> {
148 T::update_date();
149
150 ready!(self.poll_loop(cx))?;
151
152 if self.is_done() {
153 if let Some(pending) = self.conn.pending_upgrade() {
154 self.conn.take_error()?;
155 return Poll::Ready(Ok(Dispatched::Upgrade(pending)));
156 } else if should_shutdown {
157 ready!(self.conn.poll_shutdown(cx)).map_err(crate::Error::new_shutdown)?;
158 }
159 self.conn.take_error()?;
160 Poll::Ready(Ok(Dispatched::Shutdown))
161 } else {
162 Poll::Pending
163 }
164 }
165
166 fn poll_loop(&mut self, cx: &mut Context<'_>) -> Poll<crate::Result<()>> {
167 for _ in 0..16 {
173 let _ = self.poll_read(cx)?;
174 let write_ready = self.poll_write(cx)?.is_ready();
175 let flush_ready = self.poll_flush(cx)?.is_ready();
176
177 let wants_write_again = self.can_write_again() && (write_ready || flush_ready);
182
183 let wants_read_again = self.conn.wants_read_again();
192
193 if !(wants_write_again || wants_read_again) {
196 return Poll::Ready(Ok(()));
197 }
198
199 if !wants_read_again && wants_write_again {
204 if self.poll_write(cx)?.is_pending() {
208 if self.conn.has_buffered_write() {
215 let _ = self.poll_flush(cx)?;
216 }
217 return Poll::Ready(Ok(()));
218 }
219 }
220 }
221 trace!("poll_loop yielding (self = {:p})", self);
222 task::yield_now(cx).map(|never| match never {})
223 }
224
225 fn poll_read(&mut self, cx: &mut Context<'_>) -> Poll<crate::Result<()>> {
226 loop {
227 if self.is_closing {
228 return Poll::Ready(Ok(()));
229 } else if self.conn.can_read_head() {
230 ready!(self.poll_read_head(cx))?;
231 } else if let Some(mut body) = self.body_tx.take() {
232 if self.conn.can_read_body() {
233 match body.poll_ready(cx) {
234 Poll::Ready(Ok(())) => (),
235 Poll::Pending => {
236 self.body_tx.set(body);
237 return Poll::Pending;
238 }
239 Poll::Ready(Err(_canceled)) => {
240 trace!("body receiver dropped before eof, draining or closing");
243 self.conn.poll_drain_or_close_read(cx);
244 continue;
245 }
246 }
247 match self.conn.poll_read_body(cx) {
248 Poll::Ready(Some(Ok(frame))) => {
249 if frame.is_data() {
250 let chunk = frame.into_data().unwrap_or_else(|_| unreachable!());
251 match body.try_send_data(chunk) {
252 Ok(()) => {
253 self.body_tx.set(body);
254 }
255 Err(_canceled) => {
256 if self.conn.can_read_body() {
257 trace!("body receiver dropped before eof, closing");
258 self.conn.close_read();
259 }
260 }
261 }
262 } else if frame.is_trailers() {
263 let trailers =
264 frame.into_trailers().unwrap_or_else(|_| unreachable!());
265 match body.try_send_trailers(trailers) {
266 Ok(()) => {
267 self.body_tx.set(body);
268 }
269 Err(_canceled) => {
270 if self.conn.can_read_body() {
271 trace!("body receiver dropped before eof, closing");
272 self.conn.close_read();
273 }
274 }
275 }
276 } else {
277 error!("unexpected frame");
279 }
280 }
281 Poll::Ready(None) => {
282 }
284 Poll::Pending => {
285 self.body_tx.set(body);
286 return Poll::Pending;
287 }
288 Poll::Ready(Some(Err(e))) => {
289 body.send_error(crate::Error::new_body(e));
290 }
291 }
292 } else {
293 }
295 } else {
296 return self.conn.poll_read_keep_alive(cx);
297 }
298 }
299 }
300
301 fn poll_read_head(&mut self, cx: &mut Context<'_>) -> Poll<crate::Result<()>> {
302 if let Ok(()) = ready!(self.dispatch.poll_ready(cx)) {
304 } else {
305 trace!("dispatch no longer receiving messages");
306 self.close();
307 return Poll::Ready(Ok(()));
308 }
309
310 match ready!(self.conn.poll_read_head(cx)) {
312 Some(Ok((mut head, body_len, wants))) => {
313 let body = match body_len {
314 DecodedLength::ZERO => IncomingBody::empty(),
315 other => {
316 let (tx, rx) =
317 IncomingBody::new_channel(other, wants.contains(Wants::EXPECT));
318 self.body_tx.set(tx);
319 rx
320 }
321 };
322 if wants.contains(Wants::UPGRADE) {
323 let upgrade = self.conn.on_upgrade();
324 debug_assert!(!upgrade.is_none(), "empty upgrade");
325 debug_assert!(
326 head.extensions.get::<OnUpgrade>().is_none(),
327 "OnUpgrade already set"
328 );
329 head.extensions.insert(upgrade);
330 }
331 self.dispatch.recv_msg(Ok((head, body)))?;
332 Poll::Ready(Ok(()))
333 }
334 Some(Err(err)) => {
335 debug!("read_head error: {}", err);
336 self.dispatch.recv_msg(Err(err))?;
337 self.close();
341 Poll::Ready(Ok(()))
342 }
343 None => {
344 debug_assert!(self.conn.is_read_closed());
348 if self.conn.is_write_closed() {
349 self.close();
350 }
351 Poll::Ready(Ok(()))
352 }
353 }
354 }
355
356 fn poll_write(&mut self, cx: &mut Context<'_>) -> Poll<crate::Result<()>> {
357 loop {
358 if self.is_closing {
359 return Poll::Ready(Ok(()));
360 } else if self.body_rx.is_none()
361 && self.conn.can_write_head()
362 && self.dispatch.should_poll()
363 {
364 if let Some(msg) = ready!(Pin::new(&mut self.dispatch).poll_msg(cx)) {
365 let (head, body) = msg.map_err(crate::Error::new_user_service)?;
366
367 let body_type = if body.is_end_stream() {
368 self.body_rx.set(None);
369 None
370 } else {
371 let btype = body
372 .size_hint()
373 .exact()
374 .map(BodyLength::Known)
375 .or(Some(BodyLength::Unknown));
376 self.body_rx.set(Some(body));
377 btype
378 };
379 self.conn.write_head(head, body_type);
380 } else {
381 self.close();
382 return Poll::Ready(Ok(()));
383 }
384 } else if !self.conn.can_buffer_body() {
385 ready!(self.poll_flush(cx))?;
386 } else {
387 if let (Some(mut body), clear_body) =
389 OptGuard::new(self.body_rx.as_mut()).guard_mut()
390 {
391 debug_assert!(!*clear_body, "opt guard defaults to keeping body");
392 if !self.conn.can_write_body() {
393 trace!(
394 "no more write body allowed, user body is_end_stream = {}",
395 body.is_end_stream(),
396 );
397 *clear_body = true;
398 continue;
399 }
400
401 let item = ready!(body.as_mut().poll_frame(cx));
402 if let Some(item) = item {
403 let frame = item.map_err(|e| {
404 *clear_body = true;
405 crate::Error::new_user_body(e)
406 })?;
407
408 if frame.is_data() {
409 let chunk = frame.into_data().unwrap_or_else(|_| unreachable!());
410 let eos = body.is_end_stream();
411 if eos {
412 *clear_body = true;
413 if chunk.remaining() == 0 {
414 trace!("discarding empty chunk");
415 self.conn.end_body()?;
416 } else {
417 self.conn.write_body_and_end(chunk);
418 }
419 } else {
420 if chunk.remaining() == 0 {
421 trace!("discarding empty chunk");
422 continue;
423 }
424 self.conn.write_body(chunk);
425 }
426 } else if frame.is_trailers() {
427 *clear_body = true;
428 self.conn.write_trailers(
429 frame.into_trailers().unwrap_or_else(|_| unreachable!()),
430 );
431 } else {
432 trace!("discarding unknown frame");
433 }
434 } else {
435 *clear_body = true;
436 self.conn.end_body()?;
437 }
438 } else {
439 if self.conn.can_write_body() {
441 self.conn.end_body()?;
442 } else {
443 return Poll::Pending;
444 }
445 }
446 }
447 }
448 }
449
450 fn poll_flush(&mut self, cx: &mut Context<'_>) -> Poll<crate::Result<()>> {
451 self.conn.poll_flush(cx).map_err(|err| {
452 debug!("error writing: {}", err);
453 crate::Error::new_body_write(err)
454 })
455 }
456
457 fn close(&mut self) {
458 self.is_closing = true;
459 self.conn.close_read();
460 self.conn.close_write();
461 }
462
463 fn can_write_again(&mut self) -> bool {
466 !self.is_closing && self.body_rx.is_some() && self.conn.can_write_body()
467 }
468
469 fn is_done(&self) -> bool {
470 if self.is_closing {
471 return true;
472 }
473
474 let read_done = self.conn.is_read_closed();
475
476 if !T::should_read_first() && read_done {
477 true
479 } else {
480 let write_done = self.conn.is_write_closed()
481 || (!self.dispatch.should_poll() && self.body_rx.is_none());
482 read_done && write_done
483 }
484 }
485}
486
487impl<D, Bs, I, T> Future for Dispatcher<D, Bs, I, T>
488where
489 D: Dispatch<
490 PollItem = MessageHead<T::Outgoing>,
491 PollBody = Bs,
492 RecvItem = MessageHead<T::Incoming>,
493 > + Unpin,
494 D::PollError: Into<Box<dyn StdError + Send + Sync>>,
495 I: Read + Write + Unpin,
496 T: Http1Transaction + Unpin,
497 Bs: Body + 'static,
498 Bs::Error: Into<Box<dyn StdError + Send + Sync>>,
499{
500 type Output = crate::Result<Dispatched>;
501
502 #[inline]
503 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
504 self.poll_catch(cx, true)
505 }
506}
507
508struct OptGuard<'a, T>(Pin<&'a mut Option<T>>, bool);
513
514impl<'a, T> OptGuard<'a, T> {
515 fn new(pin: Pin<&'a mut Option<T>>) -> Self {
516 OptGuard(pin, false)
517 }
518
519 fn guard_mut(&mut self) -> (Option<Pin<&mut T>>, &mut bool) {
520 (self.0.as_mut().as_pin_mut(), &mut self.1)
521 }
522}
523
524impl<T> Drop for OptGuard<'_, T> {
525 fn drop(&mut self) {
526 if self.1 {
527 self.0.set(None);
528 }
529 }
530}
531
532struct SenderDropGuard(Option<crate::body::Sender>);
541
542impl SenderDropGuard {
543 fn none() -> Self {
544 SenderDropGuard(None)
545 }
546
547 fn set(&mut self, sender: crate::body::Sender) {
548 self.0 = Some(sender);
549 }
550
551 fn take(&mut self) -> Option<crate::body::Sender> {
552 self.0.take()
553 }
554}
555
556impl Drop for SenderDropGuard {
557 fn drop(&mut self) {
558 if let Some(mut sender) = self.0.take() {
559 sender.send_error(crate::Error::new_incomplete());
560 }
561 }
562}
563
564cfg_server! {
567 impl<S, B> Server<S, B>
568 where
569 S: HttpService<B>,
570 {
571 pub(crate) fn new(service: S) -> Server<S, B> {
572 Server {
573 in_flight: Box::pin(None),
574 service,
575 }
576 }
577
578 pub(crate) fn into_service(self) -> S {
579 self.service
580 }
581 }
582
583 impl<S: HttpService<B>, B> Unpin for Server<S, B> {}
585
586 impl<S, Bs> Dispatch for Server<S, IncomingBody>
587 where
588 S: HttpService<IncomingBody, ResBody = Bs>,
589 S::Error: Into<Box<dyn StdError + Send + Sync>>,
590 Bs: Body,
591 {
592 type PollItem = MessageHead<http::StatusCode>;
593 type PollBody = Bs;
594 type PollError = S::Error;
595 type RecvItem = RequestHead;
596
597 fn poll_msg(
598 mut self: Pin<&mut Self>,
599 cx: &mut Context<'_>,
600 ) -> Poll<Option<Result<(Self::PollItem, Self::PollBody), Self::PollError>>> {
601 let mut this = self.as_mut();
602 let ret = if let Some(fut) = &mut this.in_flight.as_mut().as_pin_mut() {
603 let resp = ready!(fut.as_mut().poll(cx)?);
604 let (parts, body) = resp.into_parts();
605 let head = MessageHead {
606 version: parts.version,
607 subject: parts.status,
608 headers: parts.headers,
609 extensions: parts.extensions,
610 };
611 Poll::Ready(Some(Ok((head, body))))
612 } else {
613 unreachable!("poll_msg shouldn't be called if no inflight");
614 };
615
616 this.in_flight.set(None);
618 ret
619 }
620
621 fn recv_msg(&mut self, msg: crate::Result<(Self::RecvItem, IncomingBody)>) -> crate::Result<()> {
622 let (msg, body) = msg?;
623 let mut req = Request::new(body);
624 *req.method_mut() = msg.subject.0;
625 *req.uri_mut() = msg.subject.1;
626 *req.headers_mut() = msg.headers;
627 *req.version_mut() = msg.version;
628 *req.extensions_mut() = msg.extensions;
629 let fut = self.service.call(req);
630 self.in_flight.set(Some(fut));
631 Ok(())
632 }
633
634 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), ()>> {
635 if self.in_flight.is_some() {
636 Poll::Pending
637 } else {
638 Poll::Ready(Ok(()))
639 }
640 }
641
642 fn should_poll(&self) -> bool {
643 self.in_flight.is_some()
644 }
645 }
646}
647
648cfg_client! {
651 use std::convert::Infallible;
652
653 impl<B> Client<B> {
654 pub(crate) fn new(rx: ClientRx<B>) -> Client<B> {
655 Client {
656 callback: None,
657 rx,
658 rx_closed: false,
659 }
660 }
661 }
662
663 impl<B> Dispatch for Client<B>
664 where
665 B: Body,
666 {
667 type PollItem = RequestHead;
668 type PollBody = B;
669 type PollError = Infallible;
670 type RecvItem = crate::proto::ResponseHead;
671
672 fn poll_msg(
673 mut self: Pin<&mut Self>,
674 cx: &mut Context<'_>,
675 ) -> Poll<Option<Result<(Self::PollItem, Self::PollBody), Infallible>>> {
676 let mut this = self.as_mut();
677 debug_assert!(!this.rx_closed);
678 match this.rx.poll_recv(cx) {
679 Poll::Ready(Some((req, mut cb))) => {
680 match cb.poll_canceled(cx) {
682 Poll::Ready(()) => {
683 trace!("request canceled");
684 Poll::Ready(None)
685 }
686 Poll::Pending => {
687 let (parts, body) = req.into_parts();
688 let head = RequestHead {
689 version: parts.version,
690 subject: crate::proto::RequestLine(parts.method, parts.uri),
691 headers: parts.headers,
692 extensions: parts.extensions,
693 };
694 this.callback = Some(cb);
695 Poll::Ready(Some(Ok((head, body))))
696 }
697 }
698 }
699 Poll::Ready(None) => {
700 trace!("client tx closed");
702 this.rx_closed = true;
703 Poll::Ready(None)
704 }
705 Poll::Pending => Poll::Pending,
706 }
707 }
708
709 fn recv_msg(&mut self, msg: crate::Result<(Self::RecvItem, IncomingBody)>) -> crate::Result<()> {
710 match msg {
711 Ok((msg, body)) => {
712 if let Some(cb) = self.callback.take() {
713 let res = msg.into_response(body);
714 cb.send(Ok(res));
715 Ok(())
716 } else {
717 Err(crate::Error::new_unexpected_message())
721 }
722 }
723 Err(err) => {
724 if let Some(cb) = self.callback.take() {
725 cb.send(Err(TrySendError {
726 error: err,
727 message: None,
728 }));
729 Ok(())
730 } else if !self.rx_closed {
731 self.rx.close();
732 if let Some((req, cb)) = self.rx.try_recv() {
733 trace!("canceling queued request with connection error: {}", err);
734 cb.send(Err(TrySendError {
737 error: crate::Error::new_canceled().with(err),
738 message: Some(req),
739 }));
740 Ok(())
741 } else {
742 Err(err)
743 }
744 } else {
745 Err(err)
746 }
747 }
748 }
749 }
750
751 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), ()>> {
752 match &mut self.callback {
753 Some(cb) => match cb.poll_canceled(cx) {
754 Poll::Ready(()) => {
755 trace!("callback receiver has dropped");
756 Poll::Ready(Err(()))
757 }
758 Poll::Pending => Poll::Ready(Ok(())),
759 },
760 None => Poll::Ready(Err(())),
761 }
762 }
763
764 fn should_poll(&self) -> bool {
765 self.callback.is_none()
766 }
767 }
768}
769
770#[cfg(test)]
771mod tests {
772 use super::*;
773 use crate::common::io::Compat;
774 use crate::proto::h1::ClientTransaction;
775 use std::time::Duration;
776
777 #[test]
778 fn client_read_bytes_before_writing_request() {
779 let _ = pretty_env_logger::try_init();
780
781 tokio_test::task::spawn(()).enter(|cx, _| {
782 let (io, mut handle) = tokio_test::io::Builder::new().build_with_handle();
783
784 let (mut tx, rx) = crate::client::dispatch::channel();
787 let conn = Conn::<_, bytes::Bytes, ClientTransaction>::new(Compat::new(io));
788 let mut dispatcher = Dispatcher::new(Client::new(rx), conn);
789
790 assert!(Pin::new(&mut dispatcher).poll(cx).is_pending());
792
793 handle.read(b"HTTP/1.1 200 OK\r\n\r\n");
796
797 let mut res_rx = tx
798 .try_send(crate::Request::new(IncomingBody::empty()))
799 .unwrap();
800
801 tokio_test::assert_ready_ok!(Pin::new(&mut dispatcher).poll(cx));
802 let err = tokio_test::assert_ready_ok!(Pin::new(&mut res_rx).poll(cx))
803 .expect_err("callback should send error");
804
805 match (err.error.is_canceled(), err.message.as_ref()) {
806 (true, Some(_)) => (),
807 _ => panic!("expected Canceled, got {:?}", err),
808 }
809 });
810 }
811
812 #[cfg(not(miri))]
813 #[tokio::test]
814 async fn client_flushing_is_not_ready_for_next_request() {
815 let _ = pretty_env_logger::try_init();
816
817 let (io, _handle) = tokio_test::io::Builder::new()
818 .write(b"POST / HTTP/1.1\r\ncontent-length: 4\r\n\r\n")
819 .read(b"HTTP/1.1 200 OK\r\ncontent-length: 0\r\n\r\n")
820 .wait(std::time::Duration::from_secs(2))
821 .build_with_handle();
822
823 let (mut tx, rx) = crate::client::dispatch::channel();
824 let mut conn = Conn::<_, bytes::Bytes, ClientTransaction>::new(Compat::new(io));
825 conn.set_write_strategy_queue();
826
827 let dispatcher = Dispatcher::new(Client::new(rx), conn);
828 let _dispatcher = tokio::spawn(async move { dispatcher.await });
829
830 let body = {
831 let (mut tx, body) = IncomingBody::new_channel(DecodedLength::new(4), false);
832 tx.try_send_data("reee".into()).unwrap();
833 body
834 };
835
836 let req = crate::Request::builder().method("POST").body(body).unwrap();
837
838 let res = tx.try_send(req).unwrap().await.expect("response");
839 drop(res);
840
841 assert!(!tx.is_ready());
842 }
843
844 #[cfg(not(miri))]
845 #[tokio::test]
846 async fn body_empty_chunks_ignored() {
847 let _ = pretty_env_logger::try_init();
848
849 let io = tokio_test::io::Builder::new()
850 .wait(Duration::from_secs(5))
852 .build();
853
854 let (mut tx, rx) = crate::client::dispatch::channel();
855 let conn = Conn::<_, bytes::Bytes, ClientTransaction>::new(Compat::new(io));
856 let mut dispatcher = tokio_test::task::spawn(Dispatcher::new(Client::new(rx), conn));
857
858 assert!(dispatcher.poll().is_pending());
860
861 let body = {
862 let (mut tx, body) = IncomingBody::channel();
863 tx.try_send_data("".into()).unwrap();
864 body
865 };
866
867 let _res_rx = tx.try_send(crate::Request::new(body)).unwrap();
868
869 assert!(dispatcher.poll().is_pending());
872 }
873}