Skip to content

Commit 7205737

Browse files
committed
Add an HTTP client example.
1 parent 6267034 commit 7205737

File tree

2 files changed

+121
-0
lines changed

2 files changed

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

0 commit comments

Comments
 (0)