Skip to content

Commit 5ad0dba

Browse files
authored
Merge pull request #27 from blocklessnetwork/feat/http-v2-client
feat: full http client implementation with `reqwest` like API interface
2 parents e358f09 + f77efbb commit 5ad0dba

File tree

10 files changed

+1581
-243
lines changed

10 files changed

+1581
-243
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ license = "MIT/Apache-2.0"
1010
repository = "https://github.com/blocklessnetwork/sdk-rust"
1111

1212
[dependencies]
13+
base64 = { version = "0.13", default-features = false, features = ["alloc"] }
1314
htmd = { version = "0.2.2", default-features = false }
1415
json = { version = "0.12", default-features = false }
1516
kuchikiki = { version = "0.8", default-features = false }

README.md

Lines changed: 6 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,7 @@
99
2. Use follow command for build the project.
1010

1111
```bash
12-
$ cargo build
13-
```
14-
15-
HTTP example
16-
17-
```rust
18-
use blockless_sdk::*;
19-
use json;
20-
21-
fn main() {
22-
let opts = HttpOptions::new("GET", 30, 10);
23-
let http = BlocklessHttp::open("https://demo.bls.dev/tokens", &opts);
24-
let http = http.unwrap();
25-
let body = http.get_all_body().unwrap();
26-
let body = String::from_utf8(body).unwrap();
27-
let tokens = match json::parse(&body).unwrap() {
28-
json::JsonValue::Object(o) => o,
29-
_ => panic!("must be object"),
30-
};
31-
let tokens = match tokens.get("tokens") {
32-
Some(json::JsonValue::Array(tokens)) => tokens,
33-
_ => panic!("must be array"),
34-
};
35-
tokens.iter().for_each(|s| {
36-
println!("{:?}", s.as_str());
37-
});
38-
}
12+
cargo build --release --target wasm32-wasip1
3913
```
4014

4115
## Install from [crates.io](https://crates.io/crates/blockless-sdk)
@@ -58,14 +32,14 @@ cargo build --release --target wasm32-wasip1 --example coingecko_oracle
5832
echo "bitcoin" | runtime target/wasm32-wasip1/release/examples/coingecko_oracle.wasm --permission https://api.coingecko.com/
5933
```
6034

61-
### [HTTP](./examples/httpbin.rs)
35+
### [HTTP](./examples/http_client.rs)
6236

6337
```sh
6438
# Build example
65-
cargo build --release --target wasm32-wasip1 --example httpbin
39+
cargo build --release --target wasm32-wasip1 --example http_client
6640

6741
# Run example with blockless runtime
68-
~/.bls/runtime/bls-runtime target/wasm32-wasip1/release/examples/httpbin.wasm --permission http://httpbin.org/anything
42+
~/.bls/runtime/bls-runtime target/wasm32-wasip1/release/examples/http_client.wasm --permission http://httpbin.org/anything
6943
```
7044

7145
### [LLM-MCP](./examples/llm-mcp.rs)
@@ -83,8 +57,8 @@ cargo build --release --target wasm32-wasip1 --example llm-mcp
8357

8458
| Example | Description | [Browser runtime](https://github.com/blocklessnetwork/b7s-browser) support | [Native runtime](https://github.com/blessnetwork/bls-runtime) support |
8559
| ------- | ----------- | --------------- | --------------- |
86-
| [coingecko_oracle](./examples/coingecko_oracle.rs) | Coingecko Oracle to query price of bitcoin from coingecko || |
87-
| [httpbin](./examples/httpbin.rs) | HTTP to query anything from httpbin || |
60+
| [coingecko_oracle](./examples/coingecko_oracle.rs) | Coingecko Oracle to query price of bitcoin from coingecko || |
61+
| [http_client](./examples/http_client.rs) | HTTP client demonstrating various request types (GET, POST, auth, multipart) || |
8862
| [llm](./examples/llm.rs) | LLM to chat with `Llama-3.1-8B-Instruct-q4f32_1-MLC` and `SmolLM2-1.7B-Instruct-q4f16_1-MLC` models |||
8963
| [llm-mcp](./examples/llm-mcp.rs) | LLM with MCP (Model Control Protocol) demonstrating tool integration using SSE endpoints |||
9064
| [web-scrape](./examples/web-scrape.rs) | Web Scraping to scrape content from a single URL with custom configuration overrides |||

examples/coingecko_oracle.rs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use blockless_sdk::*;
1+
use blockless_sdk::http::HttpClient;
2+
use blockless_sdk::read_stdin;
23
use serde_json::json;
34
use std::collections::HashMap;
45

@@ -19,17 +20,13 @@ fn main() {
1920
.trim();
2021

2122
// perform http request
22-
let http_opts = HttpOptions::new("GET", 30, 10);
23-
let http_res = BlocklessHttp::open(
24-
format!(
25-
"https://api.coingecko.com/api/v3/simple/price?ids={}&vs_currencies=usd",
26-
coin_id
27-
)
28-
.as_str(),
29-
&http_opts,
30-
)
31-
.unwrap();
32-
let body = http_res.get_all_body().unwrap(); // e.g. {"bitcoin":{"usd":67675}}
23+
let client = HttpClient::new();
24+
let url = format!(
25+
"https://api.coingecko.com/api/v3/simple/price?ids={}&vs_currencies=usd",
26+
coin_id
27+
);
28+
let response = client.get(&url).send().unwrap();
29+
let body = response.bytes().to_vec(); // e.g. {"bitcoin":{"usd":67675}}
3330

3431
// println!("{}", String::from_utf8(body.clone()).unwrap());
3532

examples/http_client.rs

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
use blockless_sdk::http::{get, post, HttpClient, MultipartField};
2+
use std::collections::HashMap;
3+
4+
fn main() -> Result<(), Box<dyn std::error::Error>> {
5+
println!("====================================");
6+
println!("HTTP v2 Client Demo");
7+
println!("====================================");
8+
9+
println!("\n1. GET request:");
10+
match get("https://httpbin.org/get").send() {
11+
Ok(response) => {
12+
println!("GET Status: {}", response.status());
13+
println!("GET Success: {}", response.is_success());
14+
}
15+
Err(e) => println!("GET Error: {}", e),
16+
}
17+
18+
println!("\n2. POST with JSON:");
19+
let json_data = serde_json::json!({
20+
"name": "Blockless SDK",
21+
"version": "2.0",
22+
"api_style": "reqwest-like"
23+
});
24+
match post("https://httpbin.org/post").json(&json_data)?.send() {
25+
Ok(response) => {
26+
println!("POST JSON Status: {}", response.status());
27+
if let Ok(response_json) = response.json::<serde_json::Value>() {
28+
if let Some(received_json) = response_json.get("json") {
29+
println!("Received JSON: {}", received_json);
30+
}
31+
}
32+
}
33+
Err(e) => println!("POST JSON Error: {}", e),
34+
}
35+
36+
println!("\n3. Client instance with default configuration:");
37+
let mut default_headers = HashMap::new();
38+
default_headers.insert("User-Agent".to_string(), "Blockless-SDK/2.0".to_string());
39+
default_headers.insert("Accept".to_string(), "application/json".to_string());
40+
let client = HttpClient::builder()
41+
.default_headers(default_headers)
42+
.timeout(10000)
43+
.build();
44+
match client
45+
.get("https://httpbin.org/get")
46+
.query("search", "blockless")
47+
.query("limit", "10")
48+
.query("format", "json")
49+
.send()
50+
{
51+
Ok(response) => {
52+
println!("Client GET Status: {}", response.status());
53+
if let Ok(json_data) = response.json::<serde_json::Value>() {
54+
if let Some(args) = json_data.get("args") {
55+
println!("Query params: {}", args);
56+
}
57+
}
58+
}
59+
Err(e) => println!("Client GET Error: {}", e),
60+
}
61+
62+
println!("\n4. Authentication examples:");
63+
match client
64+
.get("https://httpbin.org/basic-auth/user/pass")
65+
.basic_auth("user", "pass")
66+
.send()
67+
{
68+
Ok(response) => {
69+
println!("Basic auth status: {}", response.status());
70+
if let Ok(json_data) = response.json::<serde_json::Value>() {
71+
println!("Authenticated: {:?}", json_data.get("authenticated"));
72+
}
73+
}
74+
Err(e) => println!("Basic auth error: {}", e),
75+
}
76+
77+
match client
78+
.get("https://httpbin.org/bearer")
79+
.bearer_auth("test-token-12345")
80+
.send()
81+
{
82+
Ok(response) => {
83+
println!("Bearer auth status: {}", response.status());
84+
if let Ok(json_data) = response.json::<serde_json::Value>() {
85+
println!("Token received: {:?}", json_data.get("token"));
86+
}
87+
}
88+
Err(e) => println!("Bearer auth error: {}", e),
89+
}
90+
91+
println!("\n5. Different request body types:");
92+
let mut form_data = HashMap::new();
93+
form_data.insert("name".to_string(), "Blockless".to_string());
94+
form_data.insert("type".to_string(), "distributed computing".to_string());
95+
match client
96+
.post("https://httpbin.org/post")
97+
.form(form_data)
98+
.send()
99+
{
100+
Ok(response) => {
101+
println!("Form POST Status: {}", response.status());
102+
if let Ok(json_data) = response.json::<serde_json::Value>() {
103+
if let Some(form) = json_data.get("form") {
104+
println!("Form data received: {}", form);
105+
}
106+
}
107+
}
108+
Err(e) => println!("Form POST Error: {}", e),
109+
}
110+
111+
println!("\n6. Multipart form with file upload:");
112+
let multipart_fields = vec![
113+
MultipartField::text("description", "SDK test file"),
114+
MultipartField::file(
115+
"upload",
116+
b"Hello from Blockless SDK v2!".to_vec(),
117+
"hello.txt",
118+
Some("text/plain".to_string()),
119+
),
120+
];
121+
match client
122+
.post("https://httpbin.org/post")
123+
.multipart(multipart_fields)
124+
.send()
125+
{
126+
Ok(response) => {
127+
println!("Multipart POST Status: {}", response.status());
128+
if let Ok(json_data) = response.json::<serde_json::Value>() {
129+
if let Some(files) = json_data.get("files") {
130+
println!("Files uploaded: {}", files);
131+
}
132+
}
133+
}
134+
Err(e) => println!("Multipart POST Error: {}", e),
135+
}
136+
137+
println!("\n7. Binary data:");
138+
let binary_data = vec![0x48, 0x65, 0x6c, 0x6c, 0x6f]; // "Hello" in bytes
139+
match client
140+
.post("https://httpbin.org/post")
141+
.header("Content-Type", "application/octet-stream")
142+
.body_bytes(binary_data)
143+
.send()
144+
{
145+
Ok(response) => {
146+
println!("Binary POST Status: {}", response.status());
147+
}
148+
Err(e) => println!("Binary POST Error: {}", e),
149+
}
150+
151+
println!("\n8. Advanced request building:");
152+
match client
153+
.put("https://httpbin.org/put")
154+
.header("X-Custom-Header", "custom-value")
155+
.header("X-API-Version", "2.0")
156+
.query("action", "update")
157+
.query("id", "12345")
158+
.timeout(5000)
159+
.body("Updated data")
160+
.send()
161+
{
162+
Ok(response) => {
163+
println!("PUT Status: {}", response.status());
164+
if let Ok(json_data) = response.json::<serde_json::Value>() {
165+
if let Some(headers) = json_data.get("headers") {
166+
println!("Custom headers received: {}", headers);
167+
}
168+
}
169+
}
170+
Err(e) => println!("PUT Error: {}", e),
171+
}
172+
173+
println!("\nDemo completed 🚀");
174+
Ok(())
175+
}

examples/httpbin.rs

Lines changed: 0 additions & 26 deletions
This file was deleted.

0 commit comments

Comments
 (0)