89 lines
3.2 KiB
Rust
89 lines
3.2 KiB
Rust
// This is a port of a client from `examples/start_here.rs`. Please see that example first.
|
|
// Also... maybe brush up on some async tasks since we are going to need them here (there is an
|
|
// example on async with inferium in `examples/going_async.rs`).
|
|
//
|
|
// Features `async`, `tokio-net` and `webpki-roots` dependency must be enabled for this example to
|
|
// compile. We recommend enabling the `dev` feature when running this example.
|
|
|
|
use std::{collections::HashMap, sync::Arc};
|
|
use tokio::net::TcpStream;
|
|
use tokio_rustls::{
|
|
rustls::{
|
|
pki_types::ServerName,
|
|
ClientConfig,
|
|
RootCertStore
|
|
},
|
|
TlsConnector,
|
|
TlsStream
|
|
};
|
|
use inferium::{
|
|
h1::{
|
|
ProtocolVariant,
|
|
RequestHead,
|
|
Response,
|
|
AsyncClient
|
|
},
|
|
HeaderKey,
|
|
Method,
|
|
TokioRustls
|
|
};
|
|
|
|
async fn run_tls_handshake(raw_stream: TcpStream) -> TlsStream<TcpStream> {
|
|
let mut root_certs = RootCertStore::empty();
|
|
root_certs.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
|
|
let config = ClientConfig::builder()
|
|
.with_root_certificates(root_certs)
|
|
.with_no_client_auth();
|
|
let connector = TlsConnector::from(Arc::new(config));
|
|
let verify_server_name = ServerName::try_from("zumepro.cz").unwrap();
|
|
TlsStream::Client(connector.connect(verify_server_name, raw_stream).await.unwrap())
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
let stream = TcpStream::connect("zumepro.cz:443").await.unwrap();
|
|
let stream = run_tls_handshake(stream).await;
|
|
let conn = TokioRustls::new(stream);
|
|
let mut client = AsyncClient::<TokioRustls>::new(conn);
|
|
|
|
let to_send = RequestHead::new(
|
|
Method::GET, "/".parse().unwrap(), ProtocolVariant::HTTP1_1,
|
|
HashMap::from([
|
|
(HeaderKey::USER_AGENT, "Mozilla/5.0 (inferium)".parse().unwrap()),
|
|
(HeaderKey::HOST, "zumepro.cz".parse().unwrap()),
|
|
(HeaderKey::CONNECTION, "close".parse().unwrap())
|
|
])
|
|
);
|
|
println!("----------> Sending\n\n{to_send}\n");
|
|
client.send_request(&to_send).await.unwrap();
|
|
let response = client.receive_response().await.unwrap();
|
|
|
|
let (header, body) = match response {
|
|
Response::HeadersOnly(h) => (h, None),
|
|
Response::WithSizedBody((_, _)) => panic!(),
|
|
Response::WithChunkedBody((h, b)) => (h, Some(b)),
|
|
};
|
|
|
|
println!("----------< Received\n\n{header}\n");
|
|
|
|
if let Some(mut body) = body {
|
|
|
|
// Since our zumepro server sends bodies chunked - we will need to handle it.
|
|
// This simple loop just collects all the chunks into the res vector.
|
|
let mut res = Vec::new();
|
|
while let Some(mut chunk) = body.get_chunk_async().await.unwrap() {
|
|
// Now here is a difference between sync and async.
|
|
//
|
|
// For easy body manipulation and no redundant trait pollution, a body within an
|
|
// asynchronous stream can be sent/received using the same methods as a synchronous one,
|
|
// but with the suffix `_async`.
|
|
res.append(&mut chunk.recv_all_async().await.unwrap());
|
|
}
|
|
|
|
println!(
|
|
"----------< Body\n\n{:?}\n",
|
|
std::str::from_utf8(&res).unwrap()
|
|
);
|
|
}
|
|
}
|