#![cfg(all(feature = "framework_flow", feature = "executor_tokio"))]
use std::prelude::v1::*;
use core::error::Error;
use product_os_net::SocketAddr;
use product_os_router::ProductOSRouter;
use product_os_async_executor::{Executor, TokioExecutor};
pub async fn create_flow_http_service<S>(
socket_address: SocketAddr,
router: ProductOSRouter<S>,
with_connect_info: bool,
) -> Result<(), Box<dyn Error + Send + Sync>>
where
S: Clone + Send + Sync + 'static,
{
tracing::info!("Starting Flow HTTP server listening on {}", socket_address);
let listener = tokio::net::TcpListener::bind(socket_address)
.await
.map_err(|e| -> Box<dyn Error + Send + Sync> { Box::new(e) })?;
loop {
let (cnx, addr) = match listener.accept().await {
Ok(conn) => conn,
Err(e) => {
tracing::error!("Failed to accept connection: {:?}", e);
continue;
}
};
let router = router.clone();
tokio::spawn(async move {
let stream = hyper_util::rt::TokioIo::new(cnx);
let hyper_svc = hyper::service::service_fn(move |mut req: hyper::Request<hyper::body::Incoming>| {
let mut router = router.clone();
let peer_addr = addr;
async move {
#[cfg(all(feature = "ws", not(feature = "flow_axum_compat")))]
if hyper_tungstenite::is_upgrade_request(&req) {
if let Some(ws_handler) = router.lookup_ws_handler(req.uri().path()) {
match hyper_tungstenite::upgrade(&mut req, None) {
Ok((response, websocket)) => {
tokio::spawn(async move {
match websocket.await {
Ok(stream) => {
let ws = product_os_router::flow_impl::ws::WebSocket::new(stream);
ws_handler(ws).await;
}
Err(e) => {
tracing::warn!("WebSocket upgrade failed: {:?}", e);
}
}
});
let (parts, body) = response.into_parts();
let mapped = product_os_router::Body::from(body);
return Ok(hyper::Response::from_parts(parts, mapped));
}
Err(e) => {
tracing::warn!("WebSocket upgrade error: {:?}", e);
}
}
}
}
use product_os_router::Service;
let (mut parts, body) = req.into_parts();
#[cfg(not(feature = "flow_axum_compat"))]
let body = product_os_router::Body::from(body);
#[cfg(feature = "flow_axum_compat")]
let body = product_os_router::Body::new(body);
if with_connect_info {
parts.extensions.insert(
product_os_router::flow_impl::extractors::context::ConnectInfo(peer_addr),
);
#[cfg(feature = "flow_axum_compat")]
parts.extensions.insert(
axum::extract::ConnectInfo(peer_addr),
);
}
let req = hyper::Request::from_parts(parts, body);
router.call(req).await
}
});
match TokioExecutor::context_sync() {
Ok(executor) => {
let result = hyper_util::server::conn::auto::Builder::new(executor)
.serve_connection_with_upgrades(stream, hyper_svc)
.await;
if let Err(err) = result {
tracing::warn!("error serving connection from {}: {}", addr, err);
}
}
Err(e) => tracing::error!("Failed to get executor for connection from {}: {:?}", addr, e),
}
});
}
}