#![deny(warnings)] #![warn(rust_2018_idioms)] use std::env; use hyper::{body::HttpBody as _, Body, Request}; use tokio::io::{self, AsyncWriteExt as _}; use tokio::net::TcpStream; // A simple type alias so as to DRY. type Result = std::result::Result>; #[tokio::main] async fn main() -> Result<()> { pretty_env_logger::init(); // Some simple CLI args requirements... let url = match env::args().nth(1) { Some(url) => url, None => { println!("Usage: client "); return Ok(()); } }; // HTTPS requires picking a TLS implementation, so give a better // warning if the user tries to request an 'https' URL. let url = url.parse::().unwrap(); if url.scheme_str() != Some("http") { println!("This example only works with 'http' URLs."); return Ok(()); } fetch_url(url).await } async fn fetch_url(url: hyper::Uri) -> Result<()> { let host = url.host().expect("uri has no host"); let port = url.port_u16().unwrap_or(80); let addr = format!("{}:{}", host, port); let stream = TcpStream::connect(addr).await?; let (mut sender, conn) = hyper::client::conn::handshake(stream).await?; tokio::task::spawn(async move { if let Err(err) = conn.await { println!("Connection failed: {:?}", err); } }); let req = Request::builder().uri(url).body(Body::empty()).unwrap(); let mut res = sender.send_request(req).await?; println!("Response: {}", res.status()); println!("Headers: {:#?}\n", res.headers()); // Stream the body, writing each chunk to stdout as we get it // (instead of buffering and printing at the end). while let Some(next) = res.data().await { let chunk = next?; io::stdout().write_all(&chunk).await?; } println!("\n\nDone!"); Ok(()) }