1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
|
use std::collections::HashMap;
use std::sync::Arc;
use bstr::BString;
use hyper::body::{Body as HyperBody, HttpBody as _};
use hyper::Client as HyperClient;
use tokio::sync::Mutex;
use mlua::{Error, Lua, Result, UserData, UserDataMethods};
#[derive(Clone)]
struct BodyReader(Arc<Mutex<HyperBody>>);
impl BodyReader {
fn new(body: HyperBody) -> Self {
BodyReader(Arc::new(Mutex::new(body)))
}
}
impl UserData for BodyReader {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("read", |_, reader, ()| async move {
let mut reader = reader.0.lock().await;
if let Some(bytes) = reader.data().await {
let bytes = bytes.map_err(Error::external)?;
return Ok(Some(BString::from(bytes.as_ref())));
}
Ok(None)
});
}
}
#[tokio::main]
async fn main() -> Result<()> {
let lua = Lua::new();
let fetch_url = lua.create_async_function(|lua, uri: String| async move {
let client = HyperClient::new();
let uri = uri.parse().map_err(Error::external)?;
let resp = client.get(uri).await.map_err(Error::external)?;
let lua_resp = lua.create_table()?;
lua_resp.set("status", resp.status().as_u16())?;
let mut headers = HashMap::new();
for (key, value) in resp.headers().iter() {
headers
.entry(key.as_str())
.or_insert(Vec::new())
.push(value.to_str().unwrap());
}
lua_resp.set("headers", headers)?;
lua_resp.set("body", BodyReader::new(resp.into_body()))?;
Ok(lua_resp)
})?;
let globals = lua.globals();
globals.set("fetch_url", fetch_url)?;
let f = lua
.load(
r#"
local res = fetch_url(...)
print(res.status)
for key, vals in pairs(res.headers) do
for _, val in ipairs(vals) do
print(key..": "..val)
end
end
repeat
local body = res.body:read()
if body then
print(body)
end
until not body
"#,
)
.into_function()?;
f.call_async("http://httpbin.org/ip").await
}
|