use std::sync::Arc;
use std::time::Duration;
use foundation_core::synca::OnSignal;
use foundation_core::valtron::valtron;
use foundation_http::native::server::{HttpServer, ServerConfig};
use foundation_http::shared::app::HttpApp;
use foundation_http::shared::serve::Serve;
use foundation_netio::http::NativeHttpClient;
use foundation_connectrpc::shared::transport::Transport;
use foundation_connectrpc::{
Client, ClientOptions, ConnectResult, ConnectRpcServe, Ctx, H1Transport, HandlerOptions,
JsonCodec, ProcedureCodecs, Request, Router,
};
const PROCEDURE: &str = "/demo.CountService/Count";
#[derive(Clone, Default, Debug, serde::Serialize, serde::Deserialize)]
struct CountRequest {
count: i32,
}
#[derive(Clone, Default, Debug, serde::Serialize, serde::Deserialize)]
struct CountItem {
index: i32,
label: String,
}
fn count_serve() -> Arc<dyn Serve> {
let mut router = Router::new();
router.server_stream(
PROCEDURE,
ProcedureCodecs::<CountRequest, CountItem>::of((JsonCodec,)),
|_ctx: Ctx, req: Request<CountRequest>| async move {
let n = req.msg.count.max(0);
let items: Vec<ConnectResult<CountItem>> = (0..n)
.map(|i| Ok(CountItem { index: i, label: format!("item-{i}") }))
.collect();
Ok(futures::stream::iter(items))
},
HandlerOptions::new(),
);
Arc::new(ConnectRpcServe::new(router.into_handler()))
}
fn start_server() -> (std::net::SocketAddr, Arc<OnSignal>) {
let mut app = HttpApp::new_serve();
let serve = count_serve();
app.router.add_route_any(PROCEDURE, &serve);
let listener = std::net::TcpListener::bind("127.0.0.1:0").expect("bind loopback");
let addr = listener.local_addr().expect("local addr");
let server =
HttpServer::with_config(app, &format!("127.0.0.1:{}", addr.port()), ServerConfig::defaults());
let shutdown = Arc::new(OnSignal::new());
let shutdown_thread = shutdown.clone();
std::thread::spawn(move || server.serve_with_listener(&listener, &shutdown_thread));
while std::net::TcpStream::connect_timeout(&addr, Duration::from_millis(200)).is_err() {}
(addr, shutdown)
}
#[valtron(seed = 1, threads = 4)]
async fn main() {
let (addr, shutdown) = start_server();
let transport: Arc<dyn Transport> = Arc::new(H1Transport::new(Arc::new(NativeHttpClient::from_system())));
let url = format!("http://127.0.0.1:{}{PROCEDURE}", addr.port());
let client: Client<CountRequest, CountItem> = Client::new(
transport,
&url,
ProcedureCodecs::<CountRequest, CountItem>::of((JsonCodec,)),
ClientOptions::new().with_codec("json"),
)
.expect("build client");
let ctx = Ctx::background().with_deadline(Duration::from_secs(10));
let mut stream = client
.server_stream(ctx, Request::new(CountRequest { count: 3 }))
.await
.expect("open server stream");
let mut received = Vec::new();
while let Some(item) = stream.receive().await.expect("receive item") {
println!("streamed: index={} label={:?}", item.index, item.label);
received.push(item);
}
assert_eq!(received.len(), 3, "expected 3 streamed items");
assert_eq!(received[2].label, "item-2");
println!("server-stream round-trip verified ✓ ({} items)", received.len());
shutdown.turn_on();
}