1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
45use anyhow::{Context, Error, Result};
6use fidl::endpoints::{ProtocolMarker, Request, RequestStream};
7use fuchsia_async as fasync;
8use futures::TryStreamExt;
9use log::error;
1011/// RequestHandler handles incoming FIDL requests.
12pub trait RequestHandler<P: ProtocolMarker>: Send {
13/// Handles a request. If it returns an error, the server will shut down.
14fn handle_request(&self, request: Request<P>) -> Result<(), Error>;
15}
1617/// AsyncRequestHandler handles incoming FIDL requests asynchronously.
18#[async_trait::async_trait]
19pub trait AsyncRequestHandler<P: ProtocolMarker>: Send + Sync {
20/// Handles a request. If it returns an error, the server will shut down.
21async fn handle_request(&self, request: Request<P>) -> Result<(), Error>;
22}
2324impl<P, F> RequestHandler<P> for F
25where
26P: ProtocolMarker,
27 F: Fn(Request<P>) -> Result<(), Error> + Send,
28{
29fn handle_request(&self, request: Request<P>) -> Result<(), Error> {
30self(request)
31 }
32}
3334/// Serves all requests on `stream` using `handler`.
35///
36/// Stops and returns an error if a FIDL error occurs while reading a request,
37/// or if `handler` fails. The caller should log this error.
38pub async fn serve<S, H>(mut stream: S, handler: H) -> Result<(), Error>
39where
40S: RequestStream,
41 H: RequestHandler<S::Protocol>,
42{
43while let Some(request) = stream
44 .try_next()
45 .await
46.with_context(|| format!("error reading {} request", S::Protocol::DEBUG_NAME))?
47{
48 handler
49 .handle_request(request)
50 .with_context(|| format!("error handling {} request", S::Protocol::DEBUG_NAME))?;
51 }
52Ok(())
53}
5455/// Serves all requests on `stream` using `handler`.
56///
57/// Stops and returns an error if a FIDL error occurs while reading a request,
58/// or if `handler` fails. The caller should log this error.
59pub async fn serve_async<S, H>(mut stream: S, handler: H) -> Result<(), Error>
60where
61S: RequestStream,
62 S::Ok: Send,
63 H: AsyncRequestHandler<S::Protocol>,
64{
65while let Some(request) = stream
66 .try_next()
67 .await
68.with_context(|| format!("error reading {} request", S::Protocol::DEBUG_NAME))?
69{
70 handler
71 .handle_request(request)
72 .await
73.with_context(|| format!("error handling {} request", S::Protocol::DEBUG_NAME))?;
74 }
75Ok(())
76}
7778/// Serves all requests on `stream` concurrently using `handler`.
79///
80/// Stops and returns an error if a FIDL error occurs while reading a request,
81/// or if `handler` fails. The caller should log this error.
82pub async fn serve_async_concurrent<S, H>(
83 stream: S,
84 limit: impl Into<Option<usize>>,
85 handler: H,
86) -> Result<(), Error>
87where
88S: RequestStream,
89 S::Ok: 'static + Send,
90 H: AsyncRequestHandler<S::Protocol> + 'static,
91{
92let handler = std::sync::Arc::new(handler);
9394let fut = stream.try_for_each_concurrent(limit, |request| async {
95 handler
96 .clone()
97 .handle_request(request)
98 .await
99.with_context(|| format!("error handling {} request", S::Protocol::DEBUG_NAME))
100 .unwrap();
101102Ok(())
103 });
104105 fut.await.with_context(|| format!("error reading {} request", S::Protocol::DEBUG_NAME))?;
106107Ok(())
108}
109110/// Runs the server in the background and logs the error if one occurs.
111///
112/// This implements the most common case where FIDL service authors serve a
113/// RequestStream on some remote task using `Task::spawn(...).detach()`.
114///
115/// When using this function, prefer prefixing your program's log messages by
116/// annotating `main()` with one of the following:
117///
118/// ```
119/// #[fuchsia::main(logging = true)]
120/// #[fuchsia::main(logging_prefix = "my_prefix")]
121/// ```
122pub fn serve_detached<S, H>(stream: S, handler: H)
123where
124S: RequestStream + 'static,
125 H: RequestHandler<S::Protocol> + 'static,
126{
127 fasync::Task::spawn(async move {
128if let Err(err) = serve(stream, handler).await {
129error!("{:?}", err);
130 }
131 })
132 .detach();
133}
134135/// Runs the server in the background and logs the error if one occurs.
136///
137/// This implements the most common case where FIDL service authors serve a
138/// RequestStream on some remote task using `Task::spawn(...).detach()`.
139///
140/// When using this function, prefer prefixing your program's log messages by
141/// annotating `main()` with one of the following:
142///
143/// ```
144/// #[fuchsia::main(logging = true)]
145/// #[fuchsia::main(logging_prefix = "my_prefix")]
146/// ```
147pub fn serve_async_detached<S, H>(stream: S, handler: H)
148where
149S: RequestStream + 'static,
150 S::Ok: Send,
151 H: AsyncRequestHandler<S::Protocol> + 'static,
152{
153 fasync::Task::spawn(async move {
154if let Err(err) = serve_async(stream, handler).await {
155error!("{:?}", err);
156 }
157 })
158 .detach();
159}