|
1 |
| -//! HTTP client example with hyper in compatible mode. |
| 1 | +//! HTTP client example with hyper in poll-io mode. |
2 | 2 | //!
|
3 |
| -//! It will try to fetch http://127.0.0.1:23300/monoio and print the |
| 3 | +//! It will try to fetch http://httpbin.org/ip and print the |
4 | 4 | //! response.
|
5 |
| -//! |
6 |
| -//! Note: |
7 |
| -//! It is not recommended to use this example as a production code. |
8 |
| -//! The `hyper` require `Send` for a future and obviously the future |
9 |
| -//! is not `Send` in monoio. So we just use some unsafe code to let |
10 |
| -//! it pass which infact not a good solution but the only way to |
11 |
| -//! make it work without modifying hyper. |
12 | 5 |
|
13 |
| -use std::{future::Future, pin::Pin}; |
| 6 | +use std::io::Write; |
14 | 7 |
|
15 |
| -use monoio_compat::TcpStreamCompat; |
| 8 | +use bytes::Bytes; |
| 9 | +use http_body_util::{BodyExt, Empty}; |
| 10 | +use hyper::Request; |
| 11 | +use monoio::{io::IntoPollIo, net::TcpStream}; |
| 12 | +use monoio_compat::hyper::MonoioIo; |
16 | 13 |
|
17 |
| -#[derive(Clone)] |
18 |
| -struct HyperExecutor; |
| 14 | +type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>; |
19 | 15 |
|
20 |
| -impl<F> hyper::rt::Executor<F> for HyperExecutor |
21 |
| -where |
22 |
| - F: Future + 'static, |
23 |
| - F::Output: 'static, |
24 |
| -{ |
25 |
| - fn execute(&self, fut: F) { |
26 |
| - monoio::spawn(fut); |
27 |
| - } |
28 |
| -} |
| 16 | +async fn fetch_url(url: hyper::Uri) -> Result<()> { |
| 17 | + let host = url.host().expect("uri has no host"); |
| 18 | + let port = url.port_u16().unwrap_or(80); |
| 19 | + let addr = format!("{}:{}", host, port); |
| 20 | + let stream = TcpStream::connect(addr).await?.into_poll_io()?; |
| 21 | + let io = MonoioIo::new(stream); |
29 | 22 |
|
30 |
| -#[derive(Clone)] |
31 |
| -struct HyperConnector; |
| 23 | + let (mut sender, conn) = hyper::client::conn::http1::handshake(io).await?; |
| 24 | + monoio::spawn(async move { |
| 25 | + if let Err(err) = conn.await { |
| 26 | + println!("Connection failed: {:?}", err); |
| 27 | + } |
| 28 | + }); |
32 | 29 |
|
33 |
| -impl tower_service::Service<hyper::Uri> for HyperConnector { |
34 |
| - type Response = HyperConnection; |
| 30 | + let authority = url.authority().unwrap().clone(); |
35 | 31 |
|
36 |
| - type Error = std::io::Error; |
| 32 | + let path = url.path(); |
| 33 | + let req = Request::builder() |
| 34 | + .uri(path) |
| 35 | + .header(hyper::header::HOST, authority.as_str()) |
| 36 | + .body(Empty::<Bytes>::new())?; |
37 | 37 |
|
38 |
| - #[allow(clippy::type_complexity)] |
39 |
| - type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>; |
40 |
| - |
41 |
| - fn poll_ready( |
42 |
| - &mut self, |
43 |
| - _: &mut std::task::Context<'_>, |
44 |
| - ) -> std::task::Poll<Result<(), Self::Error>> { |
45 |
| - std::task::Poll::Ready(Ok(())) |
46 |
| - } |
| 38 | + let mut res = sender.send_request(req).await?; |
47 | 39 |
|
48 |
| - fn call(&mut self, uri: hyper::Uri) -> Self::Future { |
49 |
| - let host = uri.host().unwrap(); |
50 |
| - let port = uri.port_u16().unwrap_or(80); |
51 |
| - let address = format!("{host}:{port}"); |
| 40 | + println!("Response: {}", res.status()); |
| 41 | + println!("Headers: {:#?}\n", res.headers()); |
52 | 42 |
|
53 |
| - #[allow(clippy::type_complexity)] |
54 |
| - let b: Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>> = |
55 |
| - Box::pin(async move { |
56 |
| - let conn = monoio::net::TcpStream::connect(address).await?; |
57 |
| - let hyper_conn = HyperConnection(TcpStreamCompat::new(conn)); |
58 |
| - Ok(hyper_conn) |
59 |
| - }); |
60 |
| - unsafe { std::mem::transmute(b) } |
| 43 | + // Stream the body, writing each chunk to stdout as we get it |
| 44 | + // (instead of buffering and printing at the end). |
| 45 | + while let Some(next) = res.frame().await { |
| 46 | + let frame = next?; |
| 47 | + if let Some(chunk) = frame.data_ref() { |
| 48 | + std::io::stdout().write_all(chunk)?; |
| 49 | + } |
61 | 50 | }
|
62 |
| -} |
63 |
| - |
64 |
| -struct HyperConnection(TcpStreamCompat); |
| 51 | + println!("\n\nDone!"); |
65 | 52 |
|
66 |
| -impl tokio::io::AsyncRead for HyperConnection { |
67 |
| - fn poll_read( |
68 |
| - mut self: Pin<&mut Self>, |
69 |
| - cx: &mut std::task::Context<'_>, |
70 |
| - buf: &mut tokio::io::ReadBuf<'_>, |
71 |
| - ) -> std::task::Poll<std::io::Result<()>> { |
72 |
| - Pin::new(&mut self.0).poll_read(cx, buf) |
73 |
| - } |
| 53 | + Ok(()) |
74 | 54 | }
|
75 | 55 |
|
76 |
| -impl tokio::io::AsyncWrite for HyperConnection { |
77 |
| - fn poll_write( |
78 |
| - mut self: Pin<&mut Self>, |
79 |
| - cx: &mut std::task::Context<'_>, |
80 |
| - buf: &[u8], |
81 |
| - ) -> std::task::Poll<Result<usize, std::io::Error>> { |
82 |
| - Pin::new(&mut self.0).poll_write(cx, buf) |
83 |
| - } |
84 |
| - |
85 |
| - fn poll_flush( |
86 |
| - mut self: Pin<&mut Self>, |
87 |
| - cx: &mut std::task::Context<'_>, |
88 |
| - ) -> std::task::Poll<Result<(), std::io::Error>> { |
89 |
| - Pin::new(&mut self.0).poll_flush(cx) |
90 |
| - } |
91 |
| - |
92 |
| - fn poll_shutdown( |
93 |
| - mut self: Pin<&mut Self>, |
94 |
| - cx: &mut std::task::Context<'_>, |
95 |
| - ) -> std::task::Poll<Result<(), std::io::Error>> { |
96 |
| - Pin::new(&mut self.0).poll_shutdown(cx) |
97 |
| - } |
98 |
| -} |
99 |
| - |
100 |
| -impl hyper::client::connect::Connection for HyperConnection { |
101 |
| - fn connected(&self) -> hyper::client::connect::Connected { |
102 |
| - hyper::client::connect::Connected::new() |
103 |
| - } |
104 |
| -} |
105 |
| - |
106 |
| -#[allow(clippy::non_send_fields_in_send_ty)] |
107 |
| -unsafe impl Send for HyperConnection {} |
108 |
| - |
109 | 56 | #[monoio::main]
|
110 | 57 | async fn main() {
|
111 |
| - println!("Running http client"); |
112 |
| - let connector = HyperConnector; |
113 |
| - let client = hyper::Client::builder() |
114 |
| - .executor(HyperExecutor) |
115 |
| - .build::<HyperConnector, hyper::Body>(connector); |
116 |
| - let res = client |
117 |
| - .get("http://127.0.0.1:23300/monoio".parse().unwrap()) |
118 |
| - .await |
119 |
| - .expect("failed to fetch"); |
120 |
| - println!("Response status: {}", res.status()); |
121 |
| - let body = hyper::body::to_bytes(res.into_body()) |
122 |
| - .await |
123 |
| - .expect("failed to read body"); |
124 |
| - let body = |
125 |
| - String::from_utf8(body.into_iter().collect()).expect("failed to convert body to string"); |
126 |
| - println!("Response body: {body}"); |
| 58 | + let url = "http://httpbin.org/ip".parse::<hyper::Uri>().unwrap(); |
| 59 | + fetch_url(url).await.unwrap(); |
127 | 60 | }
|
0 commit comments