Skip to content

Commit 849206e

Browse files
getongkhvzak
andauthored
update hyper to v1, and add shell command example (#384)
Co-authored-by: Alex Orlenko <[email protected]>
1 parent 80fff4f commit 849206e

File tree

4 files changed

+106
-74
lines changed

4 files changed

+106
-74
lines changed

Cargo.toml

+4-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,10 @@ libloading = { version = "0.8", optional = true }
6363
[dev-dependencies]
6464
trybuild = "1.0"
6565
futures = "0.3.5"
66-
hyper = { version = "0.14", features = ["client", "server"] }
66+
hyper = { version = "1", features = ["client", "server"] }
67+
hyper-util = { version = "0.1.3", features = ["server", "client", "client-legacy", "tokio", "http1"] }
68+
http-body-util = "0.1.1"
69+
bytes = "1.5.0"
6770
reqwest = { version = "0.12", features = ["json"] }
6871
tokio = { version = "1.0", features = ["macros", "rt", "time"] }
6972
serde = { version = "1.0", features = ["derive"] }

README.md

+11
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,17 @@ This works using Lua [coroutines](https://www.lua.org/manual/5.3/manual.html#2.6
8484
- [HTTP Server](examples/async_http_server.rs)
8585
- [TCP Server](examples/async_tcp_server.rs)
8686

87+
88+
**shell command example**:
89+
``` shell
90+
# async http client
91+
cargo run --example async_http_client --features="async macros lua54"
92+
93+
# async http server
94+
cargo run --example async_http_server --features="async macros lua54"
95+
curl http://localhost:3000
96+
```
97+
8798
### Serialization (serde) support
8899

89100
With `serialize` feature flag enabled, `mlua` allows you to serialize/deserialize any type that implements [`serde::Serialize`] and [`serde::Deserialize`] into/from [`mlua::Value`]. In addition `mlua` provides [`serde::Serialize`] trait implementation for it (including `UserData` support).

examples/async_http_client.rs

+36-17
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,38 @@
1+
use bytes::Bytes;
2+
use http_body_util::BodyExt;
3+
use http_body_util::Empty;
4+
use hyper::body::Incoming;
5+
use hyper_util::client::legacy::Client as HyperClient;
16
use std::collections::HashMap;
27

3-
use hyper::body::{Body as HyperBody, HttpBody as _};
4-
use hyper::Client as HyperClient;
5-
68
use mlua::{chunk, ExternalResult, Lua, Result, UserData, UserDataMethods};
79

8-
struct BodyReader(HyperBody);
10+
struct BodyReader(Incoming);
911

1012
impl UserData for BodyReader {
1113
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
1214
methods.add_async_method_mut("read", |lua, reader, ()| async move {
13-
if let Some(bytes) = reader.0.data().await {
14-
let bytes = bytes.into_lua_err()?;
15-
return Some(lua.create_string(&bytes)).transpose();
15+
let mut summarize = Vec::new(); // Create a vector to accumulate the bytes
16+
17+
loop {
18+
match reader.0.frame().await {
19+
Some(Ok(bytes)) => {
20+
if let Ok(data) = bytes.into_data() {
21+
summarize.extend(data); // Append the bytes to the summarize variable
22+
}
23+
}
24+
Some(Err(_)) => break, // Break on error
25+
None => break, // Break if no more frames
26+
}
27+
}
28+
29+
if !summarize.is_empty() {
30+
// If summarize has collected data, return it as a Lua string
31+
Ok(Some(lua.create_string(&summarize)?))
32+
} else {
33+
// Return None if no data was collected
34+
Ok(None)
1635
}
17-
Ok(None)
1836
});
1937
}
2038
}
@@ -24,7 +42,8 @@ async fn main() -> Result<()> {
2442
let lua = Lua::new();
2543

2644
let fetch_url = lua.create_async_function(|lua, uri: String| async move {
27-
let client = HyperClient::new();
45+
let client =
46+
HyperClient::builder(hyper_util::rt::TokioExecutor::new()).build_http::<Empty<Bytes>>();
2847
let uri = uri.parse().into_lua_err()?;
2948
let resp = client.get(uri).await.into_lua_err()?;
3049

@@ -47,18 +66,18 @@ async fn main() -> Result<()> {
4766

4867
let f = lua
4968
.load(chunk! {
50-
local res = $fetch_url(...)
69+
local res = $fetch_url(...)
5170
print("status: "..res.status)
5271
for key, vals in pairs(res.headers) do
53-
for _, val in ipairs(vals) do
54-
print(key..": "..val)
55-
end
72+
for _, val in ipairs(vals) do
73+
print(key..": "..val)
74+
end
5675
end
5776
repeat
58-
local body = res.body:read()
59-
if body then
60-
print(body)
61-
end
77+
local body = res.body:read()
78+
if body then
79+
print(body)
80+
end
6281
until not body
6382
})
6483
.into_function()?;

examples/async_http_server.rs

+55-56
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
1-
use std::future::Future;
2-
use std::net::SocketAddr;
3-
use std::pin::Pin;
4-
use std::rc::Rc;
5-
use std::task::{Context, Poll};
6-
7-
use hyper::server::conn::AddrStream;
8-
use hyper::service::Service;
9-
use hyper::{Body, Request, Response, Server};
10-
1+
use bytes::Bytes;
2+
use http_body_util::{combinators::BoxBody, BodyExt, Empty, Full};
3+
use hyper::{body::Incoming, service::Service, Request, Response};
4+
use hyper_util::{rt::TokioIo, server::conn::auto};
115
use mlua::{
126
chunk, Error as LuaError, Function, Lua, String as LuaString, Table, UserData, UserDataMethods,
137
};
8+
use std::{future::Future, net::SocketAddr, pin::Pin, rc::Rc};
9+
use tokio::{net::TcpListener, task::LocalSet};
1410

15-
struct LuaRequest(SocketAddr, Request<Body>);
11+
struct LuaRequest(SocketAddr, Request<Incoming>);
1612

1713
impl UserData for LuaRequest {
1814
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
@@ -23,16 +19,12 @@ impl UserData for LuaRequest {
2319

2420
pub struct Svc(Rc<Lua>, SocketAddr);
2521

26-
impl Service<Request<Body>> for Svc {
27-
type Response = Response<Body>;
22+
impl Service<Request<Incoming>> for Svc {
23+
type Response = Response<BoxBody<Bytes, hyper::Error>>;
2824
type Error = LuaError;
2925
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
3026

31-
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
32-
Poll::Ready(Ok(()))
33-
}
34-
35-
fn call(&mut self, req: Request<Body>) -> Self::Future {
27+
fn call(&self, req: Request<Incoming>) -> Self::Future {
3628
// If handler returns an error then generate 5xx response
3729
let lua = self.0.clone();
3830
let lua_req = LuaRequest(self.1, req);
@@ -53,16 +45,28 @@ impl Service<Request<Body>> for Svc {
5345

5446
let body = lua_resp
5547
.get::<_, Option<LuaString>>("body")?
56-
.map(|b| Body::from(b.as_bytes().to_vec()))
57-
.unwrap_or_else(Body::empty);
48+
.map(|b| {
49+
Full::new(Bytes::copy_from_slice(b.clone().as_bytes()))
50+
.map_err(|never| match never {})
51+
.boxed()
52+
})
53+
.unwrap_or_else(|| {
54+
Empty::<Bytes>::new()
55+
.map_err(|never| match never {})
56+
.boxed()
57+
});
5858

5959
Ok(resp.body(body).unwrap())
6060
}
6161
Err(err) => {
6262
eprintln!("{}", err);
6363
Ok(Response::builder()
6464
.status(500)
65-
.body(Body::from("Internal Server Error"))
65+
.body(
66+
Full::new(Bytes::from("Internal Server Error".as_bytes()))
67+
.map_err(|never| match never {})
68+
.boxed(),
69+
)
6670
.unwrap())
6771
}
6872
}
@@ -77,16 +81,16 @@ async fn main() {
7781
// Create Lua handler function
7882
let handler: Function = lua
7983
.load(chunk! {
80-
function(req)
81-
return {
82-
status = 200,
83-
headers = {
84-
["X-Req-Method"] = req:method(),
85-
["X-Remote-Addr"] = req:remote_addr(),
86-
},
87-
body = "Hello from Lua!\n"
88-
}
89-
end
84+
function(req)
85+
return {
86+
status = 200,
87+
headers = {
88+
["X-Req-Method"] = req:method(),
89+
["X-Remote-Addr"] = req:remote_addr(),
90+
},
91+
body = "Hello from Lua!\n"
92+
}
93+
end
9094
})
9195
.eval()
9296
.expect("cannot create Lua handler");
@@ -95,31 +99,26 @@ async fn main() {
9599
lua.set_named_registry_value("http_handler", handler)
96100
.expect("cannot store Lua handler");
97101

98-
let addr = ([127, 0, 0, 1], 3000).into();
99-
let server = Server::bind(&addr).executor(LocalExec).serve(MakeSvc(lua));
100-
101-
println!("Listening on http://{}", addr);
102-
103-
// Create `LocalSet` to spawn !Send futures
104-
let local = tokio::task::LocalSet::new();
105-
local.run_until(server).await.expect("cannot run server")
106-
}
107-
108-
struct MakeSvc(Rc<Lua>);
109-
110-
impl Service<&AddrStream> for MakeSvc {
111-
type Response = Svc;
112-
type Error = hyper::Error;
113-
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
114-
115-
fn poll_ready(&mut self, _: &mut Context) -> Poll<Result<(), Self::Error>> {
116-
Poll::Ready(Ok(()))
117-
}
118-
119-
fn call(&mut self, stream: &AddrStream) -> Self::Future {
120-
let lua = self.0.clone();
121-
let remote_addr = stream.remote_addr();
122-
Box::pin(async move { Ok(Svc(lua, remote_addr)) })
102+
let addr = "127.0.0.1:3000";
103+
104+
let local = LocalSet::new();
105+
let listener = TcpListener::bind(addr).await.unwrap();
106+
loop {
107+
let (stream, peer_addr) = listener.accept().await.unwrap();
108+
let io = TokioIo::new(stream);
109+
110+
let svc = Svc(lua.clone(), peer_addr);
111+
local
112+
.run_until(async move {
113+
if let Err(err) = auto::Builder::new(LocalExec)
114+
.http1()
115+
.serve_connection(io, svc)
116+
.await
117+
{
118+
println!("Error serving connection: {:?}", err);
119+
}
120+
})
121+
.await;
123122
}
124123
}
125124

0 commit comments

Comments
 (0)