How can I use a Hyper client from a gRPC function? - rust

I have a gRPC server and need to make a HTTP GET. I'm struggling getting the futures right in the calls.
I'm trying this
fn current(
&self,
_: ::grpc::RequestOptions,
p: quote::CurrentRequest,
) -> ::grpc::SingleResponse<quote::CurrentResponse> {
let symbol = p.get_symbol();
let client = Client::new();
let fut: grpc::GrpcFuture<quote::CurrentResponse> = Box::new(
client
.get(Uri::from_static(AlphaFunction::base_url()))
.and_then(|res| res.into_body().concat2())
.and_then(|body| {
info!("body {:?}", body);
let mut r = quote::CurrentResponse::new();
// TODO: Parse body
r.set_symbol(symbol.to_string());
Ok(r)
})
.map_err(|e| e),
);
grpc::SingleResponse::new(fut)
}
But I get a bunch of errors:
expected struct `hyper::error::Error`, found enum `grpc::error::Error`
and
77 | grpc::SingleResponse::new(fut)
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `protos::quote::CurrentResponse`, found tuple

I figured it out:
let fut = client
.get(Uri::from_static(AlphaFunction::base_url()))
.and_then(|res| res.into_body().concat2())
.and_then(move |body| {
info!("body {:?}", body);
let mut r = quote::CurrentResponse::new();
r.set_symbol(symbol.to_string());
Ok(r)
})
.map_err(|e| grpc::Error::Panic(format!("{}", e)));

Related

Use hyper to pass IP address of incoming connection to stack of Services

I am trying to write a server using hyper that will pass the remote (client) address of the incoming connection down to a stack of Layers (that I have built using ServiceBuilder.
I have tried to use examples from the hyper docs and also this example from the Rust forums; however, these both
pass down data to a single handler function, not a stack of service layers
have a return type of Result<Response, Infallible>, which I don't want (I want to be able to drop a connection without returning a response).
Here is one of my tries (I have tried several approaches):
use std::{
net::SocketAddr,
time::Duration,
};
use hyper::{
Body, Request, Response, Server,
server::conn::AddrStream,
service::{
make_service_fn,
service_fn,
},
};
use tower::{
Service, ServiceBuilder,
timeout::TimeoutLayer,
};
async fn dummy_handle(req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
let response_text = format!(
"{:?} {} {}", req.version(), req.method(), req.uri()
);
let response = Response::new(Body::from(response_text));
Ok(response)
}
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let addr = SocketAddr::from(([127, 0, 0, 1], 8080));
// Dummy stack of service layers I want to wrap.
let service = ServiceBuilder::new()
.layer(TimeoutLayer::new(Duration::from_millis(1000 * 60)))
.service_fn(dummy_handle);
let make_svc = make_service_fn(|socket: &AddrStream| {
let remote_addr = socket.remote_addr();
let mut inner_svc = service.clone();
let outer_svc = service_fn(move |mut req: Request<Body>| async {
req.extensions_mut().insert(remote_addr);
inner_svc.call(req)
});
async move { outer_svc }
});
Server::bind(&addr)
.serve(make_svc)
.await?;
Ok(())
}
I understand full well that including error messages is helpful here; however, this is one of those cases where the Rust compiler spits out pages (or at least screenfuls) of cryptic stuff, so I am going to limit myself to a couple of choice examples.
First, I get this a lot:
type mismatch resolving `<impl Future<Output = [async output]> as Future>::Output == Result<_, _>`
for example, preceding this:
39 | let outer_svc = service_fn(move |mut req: Request<Body>| async {
| _____________________________________-___________________________________-
| | ____________________________________|
| ||
40 | || req.extensions_mut().insert(remote_addr);
41 | || inner_svc.call(req)
42 | || });
| || -
| ||_________|
| |__________the expected closure
| the expected `async` block
...
48 | .serve(make_svc)
| ----- ^^^^^^^^ expected struct `service::util::ServiceFn`, found enum `Result`
| |
| required by a bound introduced by this call
And then the very next error message seems to be entirely contradictory:
[ several lines identical to above elided here ]
48 | .serve(make_svc)
| ^^^^^ expected enum `Result`, found struct `service::util::ServiceFn`
I just can't figure out what the compiler wants from me.
Try this:
use std::{net::SocketAddr, time::Duration, convert::Infallible};
use hyper::{
server::conn::AddrStream,
service::{make_service_fn, service_fn},
Body, Request, Response, Server,
};
use tower::{Service, ServiceBuilder};
async fn dummy_handle(req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
let response_text = format!("{:?} {} {}", req.version(), req.method(), req.uri());
let response = Response::new(Body::from(response_text));
Ok(response)
}
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let addr = SocketAddr::from(([127, 0, 0, 1], 8080));
// Dummy stack of service layers I want to wrap.
let service = ServiceBuilder::new()
.timeout(Duration::from_millis(1000 * 60))
.service_fn(dummy_handle);
let make_svc = make_service_fn(|socket: &AddrStream| {
let remote_addr = socket.remote_addr();
let mut inner_svc = service.clone();
let outer_svc = service_fn(move |mut req: Request<Body>| {
req.extensions_mut().insert(remote_addr);
inner_svc.call(req)
});
async { Ok::<_, Infallible>(outer_svc) }
});
Server::bind(&addr).serve(make_svc).await?;
Ok(())
}
You were returning a future that returns another future:
|| async {
req.extensions_mut().insert(remote_addr);
inner_svc.call(req)
}
This is an Future<Output = Future<...>>.
Therefore, you need to turn your closure into this:
|| {
req.extensions_mut().insert(remote_addr);
inner_svc.call(req)
}
If you do not need ServiceBuilder then you can do:
use std::{
net::SocketAddr,
};
use std::convert::Infallible;
use hyper::{
Body, Request, Response, Server,
server::conn::AddrStream,
service::{
make_service_fn,
service_fn,
},
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>>
{
let addr = SocketAddr::from(([127, 0, 0, 1], 8080));
let make_svc = make_service_fn(|socket: &AddrStream| {
let remote_addr = socket.remote_addr();
async move {
Ok::<_, Infallible>(service_fn(move |req: Request<Body>| async move {
println!("remote_addr: {:?}, request: {:?}", remote_addr, req);
Ok::<_, Infallible>(
Response::new(Body::from(format!(
"{:?} {} {}", req.version(), req.method(), req.uri()
)))
)
}))
}
});
let _ = Server::bind(&addr).serve(make_svc).await?;
Ok(())
}
else
use std::{
net::SocketAddr,
time::Duration,
task::{Context, Poll},
future::Future,
pin::Pin
};
use hyper::{
http,
Body, Request, Response, Server
};
use tower::{
timeout::TimeoutLayer,
Service, ServiceBuilder,
};
#[derive(Debug)]
pub struct CustomService;
impl Service<Request<Body>> for CustomService {
type Response = Response<Body>;
type Error = http::Error;
type Future = Pin<Box<dyn Future<Output=Result<Self::Response, Self::Error>> + Send>>;
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { Poll::Ready(Ok(())) }
fn call(&mut self, req: Request<Body>) -> Self::Future {
let rsp = Response::builder();
let body = Body::from(format!("{:?} {} {}", req.version(), req.method(), req.uri()));
let rsp = rsp.status(200).body(body).unwrap();
Box::pin(async {Ok(rsp) })
}
}
#[derive(Debug)]
pub struct MakeService;
impl<T> Service<T> for MakeService {
type Response = CustomService;
type Error = std::io::Error;
type Future = Pin<Box<dyn Future<Output=Result<Self::Response, Self::Error>> + Send>>;
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { Ok(()).into() }
fn call(&mut self, _: T) -> Self::Future {
Box::pin(async { Ok(CustomService) })
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>>
{
let addr = SocketAddr::from(([127, 0, 0, 1], 8080));
let service = ServiceBuilder::new()
.layer(TimeoutLayer::new(Duration::from_secs(60)))
.service(MakeService);
let _ = Server::bind(&addr).serve(service).await?;
Ok(())
}

How to extract the argument of actix_web HttpServer::new(..) into a separte function?

I am trying to extract the factory closures into it's own function.
So instead of this
let server = HttpServer::new(|| App::new().wrap(Logger::default()))
.bind("127.0.0.1:8080")?
.run();
I'd like to move App::new()... into a new function called new_app()
let server = HttpServer::new(|| new_app())
.bind("127.0.0.1:8080")?.run();
// todo
fn new_app() { todo!() }
I was unable to use Clion IDE or VSCode do it automatically as they are unable to figure out the return type of App::new().wrap()..
However I figured the return type is something like this
pub fn new_app() -> App<
impl ServiceFactory<
ServiceRequest,
Config = (),
Response = ServiceResponse<
actix_web::middleware::logger::StreamLog<actix_web::body::AnyBody>,
>,
Error = Error,
InitError = (),
>,
actix_web::middleware::logger::StreamLog<actix_web::body::AnyBody>,
> {
App::new().wrap(Logger::default())
}
However this can not be right because the module actix_web::middleware::logger is private.
So I tried with a more "generic" type of the generic type parameters,
pub fn new_app() -> App<
impl ServiceFactory<ServiceRequest>,
impl MessageBody,
> {
App::new().wrap(Logger::default())
}
However this also doesn't compile with this error
error[E0277]: the trait bound `App<impl ServiceFactory<ServiceRequest>, impl MessageBody>: actix_service::IntoServiceFactory<_, Request>` is not satisfied
--> src/app.rs:79:78
|
79 | let server = HttpServer::new(|| new_app_2()).bind("127.0.0.1:8080")?.run();
| ^^^ the trait `actix_service::IntoServiceFactory<_, Request>` is not implemented for `App<impl ServiceFactory<ServiceRequest>, impl MessageBody>`
|
= help: the following implementations were found:
<App<T, B> as actix_service::IntoServiceFactory<actix_web::app_service::AppInit<T, B>, Request>>
It seems like a trivial work to extract some code into a function but I am not sure how to fix this. Would you help me?
I solved similar issue by implementing custom logging middleware.
Then we can use actix_web::dev::Body instead of actix_web::middleware::logger::StreamLog<actix_web::body::AnyBody>.
use actix_service::ServiceFactory;
fn app() -> App<
impl ServiceFactory<
Request = actix_web::dev::ServiceRequest,
Config = (),
Response = actix_web::dev::ServiceResponse,
Error = actix_web::Error,
InitError = (),
>,
actix_web::dev::Body, // <- This
> {
App::new()
.wrap(middlewares::logging::Logging)
.service(index)
}
As a side note,
You can implement middleware referring official examples https://github.com/actix/examples/tree/master/basics/middleware/src or my code below that includes request & response log.
#![allow(unused_imports)]
use std::pin::Pin;
use std::task::{Context, Poll};
use std::rc::Rc;
use std::cell::RefCell;
use futures::{future::{ok, Future, Ready}, stream::StreamExt};
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use serde_json::{Value, json};
use actix_service::{Service, Transform};
use actix_web::{
dev::{ServiceRequest, ServiceResponse},
web::{Bytes, BytesMut},
web,
body::{Body, BodySize, MessageBody, ResponseBody},
http::Version,
Error, HttpMessage,
};
use actix_http::h1::{Payload};
use chrono::{Utc, DateTime, Date, NaiveDateTime};
// There are two steps in middleware processing.
// 1. Middleware initialization, middleware factory gets called with
// next service in chain as parameter.
// 2. Middleware's call method gets called with normal request.
pub struct Logging;
// Middleware factory is `Transform` trait from actix-service crate
// `S` - type of the next service
// `B` - type of response's body
impl<S, B> Transform<S> for Logging
where
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
S::Future: 'static,
B: MessageBody + Unpin + 'static,
{
type Request = ServiceRequest;
type Response = ServiceResponse<B>;
type Error = Error;
type InitError = ();
type Transform = LoggingMiddleware<S>;
type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future {
ok(LoggingMiddleware {
service: Rc::new(RefCell::new(service))
})
}
}
pub struct LoggingMiddleware<S> {
service: Rc<RefCell<S>>,
}
impl<S, B> Service for LoggingMiddleware<S>
where
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
S::Future: 'static,
B: MessageBody + Unpin + 'static,
{
type Request = ServiceRequest;
type Response = ServiceResponse<B>;
type Error = Error;
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready(cx)
}
fn call(&mut self, mut req: ServiceRequest) -> Self::Future {
let mut svc = self.service.clone();
Box::pin(async move {
let begin = Utc::now();
let path = req.path().to_string();
let method = req.method().as_str().to_string();
let queries = req.query_string().to_string();
let ip = req.head().peer_addr;
let protocol = match req.version() {
Version::HTTP_09 => "http/0.9",
Version::HTTP_10 => "http/1.0",
Version::HTTP_11 => "http/1.1",
Version::HTTP_2 => "http/2.0",
Version::HTTP_3 => "http/3.0",
_ => "UNKNOWN",
};
// Request headers
let mut domain = String::new();
let mut user_agent = String::new();
let mut headers: HashMap<&str, &str> = HashMap::new();
for (k, v) in req.headers().iter() {
if let Ok(inner) = v.to_str() {
let key = k.as_str();
headers.insert(key, inner);
match key {
"host" => { domain = inner.to_string() },
"user-agent" => { user_agent = inner.to_string() },
_ => {},
}
}
}
let req_headers = json!(headers).to_string();
let req_body = get_request_body(&mut req).await;
let mut parsed = parse_body(req_body.unwrap());
let req_body: Option<String> = if ! parsed.is_object() {
None
} else {
/*
// my code
// Mask some words for security, like `password`
for k in vec!["password"] {
let obj = parsed.as_object_mut().unwrap();
if let Some(p) = obj.get_mut(k) {
*p = json!("MASKED_FOR_SECURITY");
}
}
*/
Some(parsed.to_string())
};
// DbPool
/*
// my code
let pool = req.app_data::<web::Data<DbPool>>().map(|p| p.clone());
*/
// Exec main function and wait response generated
let mut res = svc.call(req).await?;
let duration = (Utc::now() - begin).num_microseconds();
let status_code = res.status();
// Response headers
let mut headers: HashMap<&str, &str> = HashMap::new();
for (k, v) in res.headers().iter() {
if let Ok(inner) = v.to_str() {
headers.insert(k.as_str(), inner);
}
}
let res_headers = json!(headers).to_string();
// Get response body
let mut res_body = BytesMut::new();
let mut stream = res.take_body();
while let Some(chunk) = stream.next().await {
res_body.extend_from_slice(&chunk?);
}
// Logging
println!("req.domain : {:?}", domain);
println!("req.user_agent : {:?}", user_agent);
println!("req.ip : {:?}", ip);
println!("req.path : {:?}", path);
println!("req.method : {:?}", method);
println!("req.headers: {:?}", req_headers);
println!("req.query : {:?}", queries);
println!("req.body : {:?}", req_body);
println!("duration : {:?}", duration);
println!("res.status : {:?}", status_code);
println!("res.headers: {:?}", res_headers);
println!("res.body : {:?}", res_body);
/*
// my code
let a = AccessLog {
id: None,
protocol: Some(protocol.to_string()).into_iter().find(|v| v != ""),
domain: Some(domain).into_iter().find(|v| v != ""),
ip: ip.map(|inner| inner.to_string()).into_iter().find(|v| v != ""),
method: Some(method).into_iter().find(|v| v != ""),
path: Some(path.to_string()).into_iter().find(|v| v != ""),
query: Some(queries).into_iter().find(|v| v != ""),
user_agent: Some(user_agent),
req_headers: Some(req_headers).into_iter().find(|v| v != ""),
req_body: req_body,
duration: duration.map(|inner| inner as i32),
status_code: Some(status_code.as_u16() as i32),
res_headers: Some(res_headers).into_iter().find(|v| v != ""),
res_body: String::from_utf8(res_body.clone().to_vec()).into_iter().find(|v| v != ""),
others: None,
requested_at: Some(begin.with_timezone(&*TIMEZONE).naive_local()),
created_at: None,
};
if let Some(pool) = pool {
if let Ok(conn) = pool.get() {
if let Err(e) = a.create(&conn) {
eprintln!("database err: {:?}", e);
};
}
}
*/
// return original response body
Ok(res.map_body(|_, _b| ResponseBody::Other(Body::from(res_body))))
})
}
}
#[pin_project::pin_project(PinnedDrop)]
pub struct BodyLogger<B> {
#[pin]
body: ResponseBody<B>,
body_accum: BytesMut,
}
#[pin_project::pinned_drop]
impl<B> PinnedDrop for BodyLogger<B> {
fn drop(self: Pin<&mut Self>) {
println!("response body: {:?}", self.body_accum);
}
}
impl<B: MessageBody> MessageBody for BodyLogger<B> {
fn size(&self) -> BodySize {
self.body.size()
}
fn poll_next(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Bytes, Error>>> {
let this = self.project();
match this.body.poll_next(cx) {
Poll::Ready(Some(Ok(chunk))) => {
this.body_accum.extend_from_slice(&chunk);
Poll::Ready(Some(Ok(chunk)))
}
Poll::Ready(Some(Err(e))) => Poll::Ready(Some(Err(e))),
Poll::Ready(None) => Poll::Ready(None),
Poll::Pending => Poll::Pending,
}
}
}
async fn get_request_body(req: &mut ServiceRequest) -> Result<BytesMut, Error> {
// Get body as bytes
let mut bytes: BytesMut = BytesMut::new();
let mut body = req.take_payload();
while let Some(chunk) = body.next().await {
bytes.extend_from_slice(&chunk?);
}
// Set body again
let (_, mut payload) = Payload::create(true);
payload.unread_data(web::Bytes::from(bytes.clone()));
req.set_payload(payload.into());
Ok(bytes)
}
#[derive(Debug, Deserialize, Serialize)]
struct Password {
password: String,
}
fn parse_body(body: BytesMut) -> Value {
let json_parsed = serde_json::from_slice::<Value>(&body);
if let Ok(b) = json_parsed {
return b
}
// let query_parsed = serde_qs::from_bytes::<Password>(&body);
json!(null)
}

How to print a response body in actix_web middleware?

I'd like to write a very simple middleware using actix_web framework but it's so far beating me on every front.
I have a skeleton like this:
let result = actix_web::HttpServer::new(move || {
actix_web::App::new()
.wrap_fn(move |req, srv| {
srv.call(req).map(move|res| {
println!("Got response");
// let s = res.unwrap().response().body();
// ???
res
})
})
})
.bind("0.0.0.0:8080")?
.run()
.await;
and I can access ResponseBody type via res.unwrap().response().body() but I don't know what can I do with this.
Any ideas?
This is an example of how I was able to accomplish this with 4.0.0-beta.14:
use std::cell::RefCell;
use std::pin::Pin;
use std::rc::Rc;
use std::collections::HashMap;
use std::str;
use erp_contrib::{actix_http, actix_web, futures, serde_json};
use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform};
use actix_web::{HttpMessage, body, http::StatusCode, error::Error ,HttpResponseBuilder};
use actix_http::{h1::Payload, header};
use actix_web::web::{BytesMut};
use futures::future::{ok, Future, Ready};
use futures::task::{Context, Poll};
use futures::StreamExt;
use crate::response::ErrorResponse;
pub struct UnhandledErrorResponse;
impl<S: 'static> Transform<S, ServiceRequest> for UnhandledErrorResponse
where
S: Service<ServiceRequest, Response = ServiceResponse, Error = Error>,
S::Future: 'static,
{
type Response = ServiceResponse;
type Error = Error;
type Transform = UnhandledErrorResponseMiddleware<S>;
type InitError = ();
type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future {
ok(UnhandledErrorResponseMiddleware { service: Rc::new(RefCell::new(service)), })
}
}
pub struct UnhandledErrorResponseMiddleware<S> {
service: Rc<RefCell<S>>,
}
impl<S> Service<ServiceRequest> for UnhandledErrorResponseMiddleware<S>
where
S: Service<ServiceRequest, Response = ServiceResponse, Error = Error> + 'static,
S::Future: 'static,
{
type Response = ServiceResponse;
type Error = Error;
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready(cx)
}
fn call(&self, mut req: ServiceRequest) -> Self::Future {
let svc = self.service.clone();
Box::pin(async move {
/* EXTRACT THE BODY OF REQUEST */
let mut request_body = BytesMut::new();
while let Some(chunk) = req.take_payload().next().await {
request_body.extend_from_slice(&chunk?);
}
let mut orig_payload = Payload::empty();
orig_payload.unread_data(request_body.freeze());
req.set_payload(actix_http::Payload::from(orig_payload));
/* now process the response */
let res: ServiceResponse = svc.call(req).await?;
let content_type = match res.headers().get("content-type") {
None => { "unknown"}
Some(header) => {
match header.to_str() {
Ok(value) => {value}
Err(_) => { "unknown"}
}
}
};
return match res.response().error() {
None => {
Ok(res)
}
Some(error) => {
if content_type.to_uppercase().contains("APPLICATION/JSON") {
Ok(res)
} else {
let error = error.to_string();
let new_request = res.request().clone();
/* EXTRACT THE BODY OF RESPONSE */
let _body_data =
match str::from_utf8(&body::to_bytes(res.into_body()).await?){
Ok(str) => {
str
}
Err(_) => {
"Unknown"
}
};
let mut errors = HashMap::new();
errors.insert("general".to_string(), vec![error]);
let new_response = match ErrorResponse::new(&false, errors) {
Ok(response) => {
HttpResponseBuilder::new(StatusCode::BAD_REQUEST)
.insert_header((header::CONTENT_TYPE, "application/json"))
.body(serde_json::to_string(&response).unwrap())
}
Err(_error) => {
HttpResponseBuilder::new(StatusCode::BAD_REQUEST)
.insert_header((header::CONTENT_TYPE, "application/json"))
.body("An unknown error occurred.")
}
};
Ok(ServiceResponse::new(
new_request,
new_response
))
}
}
}
})
}
}
The extraction of the Request Body is straightforward and similar to how Actix example's illustrate. However, with the update to version Beta 14, pulling the bytes directly from AnyBody has changed with the introduction of BoxedBody. Fortunately, I found a utility function body::to_bytes (use actix_web::body::to_bytes) which does a good job. It's current implementation looks like this:
pub async fn to_bytes<B: MessageBody>(body: B) -> Result<Bytes, B::Error> {
let cap = match body.size() {
BodySize::None | BodySize::Sized(0) => return Ok(Bytes::new()),
BodySize::Sized(size) => size as usize,
// good enough first guess for chunk size
BodySize::Stream => 32_768,
};
let mut buf = BytesMut::with_capacity(cap);
pin!(body);
poll_fn(|cx| loop {
let body = body.as_mut();
match ready!(body.poll_next(cx)) {
Some(Ok(bytes)) => buf.extend_from_slice(&*bytes),
None => return Poll::Ready(Ok(())),
Some(Err(err)) => return Poll::Ready(Err(err)),
}
})
.await?;
Ok(buf.freeze())
}
which I believe should be fine to extract the body in this way, as the body is extracted from the body stream by to_bytes().
If someone has a better way, let me know, but it was a little bit of pain, and I only had recently determined how to do it in Beta 13 when it switched to Beta 14.
This particular example intercepts errors and rewrites them to JSON format if they're not already json format. This would be the case, as an example, if an error occurs outside of a handler, such as parsing JSON in the handler function itself _data: web::Json<Request<'a, LoginRequest>> and not in the handler body. Extracting the Request Body and Response Body is not necessary to accomplish the goal, and is just here for illustration.

How to peek a tokio TcpStream from a TcpListener?

I am writing a server using rustls and hyper and wish to peek and then parse the TcpStream to then accept the corresponding tokio_rustls::TlsAcceptor I want. However, this leads me to use both async and non async functions (tokio::net::TcpStream::peek and tokio_rustls::TlsAcceptor::accept) on the stream, which as been causing me trouble. Simply adding an async block for the peek function gives me an "unused implementer of `core::future::future::Future` that must be used" error and changing move to async move does not work.
I'm wondering if there is some way to get around this, perhaps by not using and_then()?
// Dependencies: futures-util = "0.3.1", rustls = "0.18"
// tokio = {version = "0.2", features = ["full"]}, tokio-rustls = "0.14.0"
use tokio::net::{TcpListener, TcpStream};
use tokio_rustls::server::TlsStream;
use tokio_rustls::TlsAcceptor;
use std::{sync, io};
use futures_util::{
future::TryFutureExt,
stream::{StreamExt, TryStreamExt},
};
#[tokio::main]
async fn run_server() -> Result<(), Box<dyn std::error::Error + Send + Sync>>{
let addr = format!("127.0.0.1:{}", 8000);
let mut tcp = TcpListener::bind(&addr).await?;
let tls_config = sync::Arc::new(rustls::ServerConfig::new(rustls::NoClientAuth::new()));
let tls_acceptor = TlsAcceptor::from(tls_config);
let mut v = vec![0u8; 16 * 1024];
// main focus of question
let incoming_tls_stream = tcp
.incoming()
.map_err(|e| error(format!("Incoming failed: {:?}", e)))
.and_then(move |mut s: TcpStream| {
let n: usize = s.peek(&mut v).await.unwrap();
println!("{:}", n);
// parse something from stream
let parsed = do_something(&v[..n]);
println!("{:}", parsed);
tls_acceptor.accept(s).map_err(|e| {
println!("Client-connection error...");
error(format!("TLS Error: {:?}", e))
})
})
.boxed();
// ...
return Ok(());
}
fn main() {
if let Err(e) = run_server() {
eprintln!("FAILED: {}", e);
std::process::exit(1);
}
}
fn error(err: String) -> io::Error {
io::Error::new(io::ErrorKind::Other, err)
}
fn do_something(bytes: &[u8]) -> &str {
return "test";
}

Example usage of hyper with bb8 and postgres

I want to use hyper with bb8 and tokio-postgres. In every request I want to acquire a new connection from the pool. Can anybody provide me some example for this scenario?
Currently I do it like this:
fn main() {
let addr = "127.0.0.1:3000".parse().unwrap();
let pg_mgr =
PostgresConnectionManager::new("postgresql://auth:auth#localhost:5433/auth", NoTls);
rt::run(future::lazy(move || {
Pool::builder()
.build(pg_mgr)
.map_err(|e| eprintln!("Database error: {}", e))
.and_then(move |pool| {
let service = || service_fn(|req| router(req, pool.clone()));
let server = Server::bind(&addr)
.serve(service)
.map_err(|e| eprintln!("Server error: {}", e));
println!("Listening on http://{}", addr);
server
})
}))
}
fn router(
_req: Request<Body>,
_pool: Pool<PostgresConnectionManager<NoTls>>,
) -> Result<Response<Body>, hyper::Error> {
// do some staff with pool
}
But it won't compile:
error[E0597]: `pool` does not live long enough
--> src/main.rs:22:63
|
22 | let service = || service_fn(|req| router(req, pool.clone()));
| -- -----------------------------^^^^----------
| | | |
| | | borrowed value does not live long enough
| | returning this value requires that `pool` is borrowed for `'static`
| value captured here
...
30 | })
| - `pool` dropped here while still borrowed
What am I doing wrong? How to make my case work correctly?
The solution is pretty simple but to understand the problem I want to provide some additional info...
When you call and_then on a future to get the result, it passes the value of the variable to the closure passed to and_then which gives you ownership of that data.
The method serve on hypers builder (returned by Server::bind), expects for the closure to have a static lifetime.
Now to address the problem:
Good: pass the value of the closure into serve, this moves it, transferring the ownership.
Good: service_fn is defined outside of the and_then closure so that function lives long enough
Bad: The closure uses the local variable pool to pass it to the service_fn.
To resolve the problem, just move the local data into your closure like so:
let service = move || service_fn(|req| router(req, pool));
Solution found here
The simplest solution looks like:
fn main() {
let addr = "127.0.0.1:3000".parse().unwrap();
let pg_mgr =
PostgresConnectionManager::new("postgresql://auth:auth#localhost:5433/auth", NoTls);
rt::run(future::lazy(move || {
Pool::builder()
.build(pg_mgr)
.map_err(|_| eprintln!("kek"))
.and_then(move |pool| {
let service = move || {
let pool = pool.clone();
service_fn(move |req| router(req, &pool))
};
let server = Server::bind(&addr)
.serve(service)
.map_err(|e| eprintln!("Server error: {}", e));
println!("Listening on http://{}", addr);
server
})
}))
}
fn router(
_req: Request<Body>,
_pool: &Pool<PostgresConnectionManager<NoTls>>,
) -> impl Future<Item = Response<Body>, Error = hyper::Error> {
// some staff
}
It is also possible to construct service outside of rt::run with Arc and Mutex:
fn main() {
let addr = "127.0.0.1:3000".parse().unwrap();
let pg_mgr =
PostgresConnectionManager::new("postgresql://auth:auth#localhost:5433/auth", NoTls);
let pool: Arc<Mutex<Option<Pool<PostgresConnectionManager<NoTls>>>>> =
Arc::new(Mutex::new(None));
let pool2 = pool.clone();
let service = move || {
let pool = pool.clone();
service_fn(move |req| {
let locked = pool.lock().unwrap();
let pool = locked
.as_ref()
.expect("bb8 should be initialized before hyper");
router(req, pool)
})
};
rt::run(future::lazy(move || {
Pool::builder()
.build(pg_mgr)
.map_err(|_| eprintln!("kek"))
.and_then(move |pool| {
*pool2.lock().unwrap() = Some(pool);
let server = Server::bind(&addr)
.serve(service)
.map_err(|e| eprintln!("Server error: {}", e));
println!("Listening on http://{}", addr);
server
})
}))
}
fn router(
_req: Request<Body>,
_pool: &Pool<PostgresConnectionManager<NoTls>>,
) -> impl Future<Item = Response<Body>, Error = hyper::Error> {
// some staff
}

Resources