Skip to content

Commit 4da851d

Browse files
committed
Add an HTTP client example.
1 parent a49c9dc commit 4da851d

File tree

2 files changed

+123
-0
lines changed

2 files changed

+123
-0
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ wstd-macro.workspace = true
2323

2424
[dev-dependencies]
2525
anyhow.workspace = true
26+
clap.workspace = true
2627
futures-lite.workspace = true
28+
humantime.workspace = true
2729
serde_json.workspace = true
2830

2931
[workspace]
@@ -49,8 +51,10 @@ authors = [
4951
[workspace.dependencies]
5052
anyhow = "1"
5153
cargo_metadata = "0.18.1"
54+
clap = { version = "4.5.26", features = ["derive"] }
5255
futures-core = "0.3.19"
5356
futures-lite = "1.12.0"
57+
humantime = "2.1.0"
5458
heck = "0.5"
5559
http = "1.1"
5660
itoa = "1"

examples/http_client.rs

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
use anyhow::{anyhow, Result};
2+
use clap::{ArgAction, Parser};
3+
use wstd::http::{
4+
body::{IncomingBody, StreamedBody},
5+
request::Builder,
6+
Body, Client, Method, Request, Response, Uri,
7+
};
8+
9+
/// Simple HTTP client
10+
///
11+
/// A simple command-line HTTP client, implemented using `wstd`, using WASI.
12+
#[derive(Parser, Debug)]
13+
#[command(version, about)]
14+
struct Args {
15+
/// The URL to request
16+
url: Uri,
17+
18+
/// Forward stdin to the request body
19+
#[arg(long)]
20+
body: bool,
21+
22+
/// Add a header to the request
23+
#[arg(long = "header", action = ArgAction::Append, value_name = "HEADER")]
24+
headers: Vec<String>,
25+
26+
/// Method of the request
27+
#[arg(long, default_value = "GET")]
28+
method: Method,
29+
30+
/// Set the connect timeout
31+
#[arg(long, value_name = "DURATION")]
32+
connect_timeout: Option<humantime::Duration>,
33+
34+
/// Set the first-byte timeout
35+
#[arg(long, value_name = "DURATION")]
36+
first_byte_timeout: Option<humantime::Duration>,
37+
38+
/// Set the between-bytes timeout
39+
#[arg(long, value_name = "DURATION")]
40+
between_bytes_timeout: Option<humantime::Duration>,
41+
}
42+
43+
#[wstd::main]
44+
async fn main() -> Result<()> {
45+
let args = Args::parse();
46+
47+
// Create and configure the `Client`
48+
49+
let mut client = Client::new();
50+
51+
if let Some(connect_timeout) = args.connect_timeout {
52+
client.set_connect_timeout(*connect_timeout);
53+
}
54+
if let Some(first_byte_timeout) = args.first_byte_timeout {
55+
client.set_first_byte_timeout(*first_byte_timeout);
56+
}
57+
if let Some(between_bytes_timeout) = args.between_bytes_timeout {
58+
client.set_between_bytes_timeout(*between_bytes_timeout);
59+
}
60+
61+
// Create and configure the request.
62+
63+
let mut request = Request::builder();
64+
65+
request = request.uri(args.url).method(args.method);
66+
67+
for header in args.headers {
68+
let mut parts = header.splitn(2, ": ");
69+
let key = parts.next().unwrap();
70+
let value = parts
71+
.next()
72+
.ok_or_else(|| anyhow!("headers must be formatted like \"key: value\""))?;
73+
request = request.header(key, value);
74+
}
75+
76+
// Send the request.
77+
78+
async fn send_request<B: Body>(
79+
client: &Client,
80+
request: Builder,
81+
body: B,
82+
) -> Result<Response<IncomingBody>> {
83+
let request = request.body(body)?;
84+
85+
eprintln!("> {} / {:?}", request.method(), request.version());
86+
for (key, value) in request.headers().iter() {
87+
let value = String::from_utf8_lossy(value.as_bytes());
88+
eprintln!("> {key}: {value}");
89+
}
90+
91+
Ok(client.send(request).await?)
92+
}
93+
let response = if args.body {
94+
send_request(&client, request, StreamedBody::new(wstd::io::stdin())).await
95+
} else {
96+
send_request(&client, request, wstd::io::empty()).await
97+
}?;
98+
99+
// Print the response.
100+
101+
eprintln!("< {:?} {}", response.version(), response.status());
102+
for (key, value) in response.headers().iter() {
103+
let value = String::from_utf8_lossy(value.as_bytes());
104+
eprintln!("< {key}: {value}");
105+
}
106+
107+
let mut body = response.into_body();
108+
wstd::io::copy(&mut body, wstd::io::stdout()).await?;
109+
110+
let trailers = body.finish().await?;
111+
if let Some(trailers) = trailers {
112+
for (key, value) in trailers.iter() {
113+
let value = String::from_utf8_lossy(value.as_bytes());
114+
eprintln!("< {key}: {value}");
115+
}
116+
}
117+
118+
Ok(())
119+
}

0 commit comments

Comments
 (0)