目前,我们正在将c中制作的API网关引擎切换到tokio, hyper, rustls of rust。
在分析hyper_rustls (tokio_rustls)提供的echo server示例时,有不明白的地方,请求帮助。(我正在努力没有太多的例子来参考)
https://github.com/rustls/hyper-rustls/blob/main/examples/server.rs
这是我在想的流程:当收到一个正文较大的POST请求时,读取所有http正文→读取到content-length后,执行传递给make_service_fn的未来代码(示例中的echo操作)。
但是,一旦接收到请求,就执行传递给make_service_fn的代码,并向客户端发送响应,并且执行tokio::io::AsyncRead特性的poll_read函数多次。
问:make_fn_service代码到底什么时候运行,这是我可以控制的吗?
Q:当使用hyper时,它似乎将body积累在内存中。因此,如果正文尺寸非常大,我想把它下载到一个单独的文件。有没有办法直接控制身体每次来?
- 我可以使用hyper::body::HttpBody特性吗?
关于Q1:
let service = make_service_fn(|_| async { Ok::<_, io::Error>(service_fn(echo)) });
调用make_fn_service
将异步函数转换为可以传递给serve()
的东西。它得到一个类型为&AddrStream
的参数,并可以做各种花哨的东西,如过滤和节流,但如果您不需要这些,只需调用service_fn
与您的异步函数。
那么您的函数,在示例中是echo
,将在每个客户端请求中调用一次。
关于Q2:
正文未在内存中积累:
*response.body_mut() = req.into_body();
但是这些是Body
类型的,实现了Stream<Item=Result<Bytes>>
,这些Bytes
是请求/响应的主体块。
通过将一个Stream
分配给另一个Stream
,一个非常大的ping应该通过echo
函数轻松地流式传输,一次一个块。
如果你想自己管理数据,你可以轮询流(StreamExt::next()
)并单独处理每个体块。请不要调用Body::to_bytes()
或Body::aggregate()
。
关于使用HttpBody
性状:
当然你可以直接使用它,但它不是微不足道的。我认为它通常是这样实现的,以便您可以直接从请求中获得例如JSON对象,或XML或urlencoded映射或任何您的Content-Type指示,而无需做中间字节数组和解析。
但是正如你可能猜到的那样,在异步模式下处理巨大的XML/JSON有效负载并不容易。如果你真的需要,你可以让它变得更简单,只是驱动普通Body
的字节块。