fidl_server/
server.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use anyhow::{Context, Error, Result};
use fidl::endpoints::{ProtocolMarker, Request, RequestStream};
use fuchsia_async as fasync;
use futures::TryStreamExt;
use tracing::error;

/// RequestHandler handles incoming FIDL requests.
pub trait RequestHandler<P: ProtocolMarker>: Send {
    /// Handles a request. If it returns an error, the server will shut down.
    fn handle_request(&self, request: Request<P>) -> Result<(), Error>;
}

/// AsyncRequestHandler handles incoming FIDL requests asynchronously.
#[async_trait::async_trait]
pub trait AsyncRequestHandler<P: ProtocolMarker>: Send + Sync {
    /// Handles a request. If it returns an error, the server will shut down.
    async fn handle_request(&self, request: Request<P>) -> Result<(), Error>;
}

impl<P, F> RequestHandler<P> for F
where
    P: ProtocolMarker,
    F: Fn(Request<P>) -> Result<(), Error> + Send,
{
    fn handle_request(&self, request: Request<P>) -> Result<(), Error> {
        self(request)
    }
}

/// Serves all requests on `stream` using `handler`.
///
/// Stops and returns an error if a FIDL error occurs while reading a request,
/// or if `handler` fails. The caller should log this error.
pub async fn serve<S, H>(mut stream: S, handler: H) -> Result<(), Error>
where
    S: RequestStream,
    H: RequestHandler<S::Protocol>,
{
    while let Some(request) = stream
        .try_next()
        .await
        .with_context(|| format!("error reading {} request", S::Protocol::DEBUG_NAME))?
    {
        handler
            .handle_request(request)
            .with_context(|| format!("error handling {} request", S::Protocol::DEBUG_NAME))?;
    }
    Ok(())
}

/// Serves all requests on `stream` using `handler`.
///
/// Stops and returns an error if a FIDL error occurs while reading a request,
/// or if `handler` fails. The caller should log this error.
pub async fn serve_async<S, H>(mut stream: S, handler: H) -> Result<(), Error>
where
    S: RequestStream,
    S::Ok: Send,
    H: AsyncRequestHandler<S::Protocol>,
{
    while let Some(request) = stream
        .try_next()
        .await
        .with_context(|| format!("error reading {} request", S::Protocol::DEBUG_NAME))?
    {
        handler
            .handle_request(request)
            .await
            .with_context(|| format!("error handling {} request", S::Protocol::DEBUG_NAME))?;
    }
    Ok(())
}

/// Serves all requests on `stream` concurrently using `handler`.
///
/// Stops and returns an error if a FIDL error occurs while reading a request,
/// or if `handler` fails. The caller should log this error.
pub async fn serve_async_concurrent<S, H>(
    stream: S,
    limit: impl Into<Option<usize>>,
    handler: H,
) -> Result<(), Error>
where
    S: RequestStream,
    S::Ok: 'static + Send,
    H: AsyncRequestHandler<S::Protocol> + 'static,
{
    let handler = std::sync::Arc::new(handler);

    let fut = stream.try_for_each_concurrent(limit, |request| async {
        handler
            .clone()
            .handle_request(request)
            .await
            .with_context(|| format!("error handling {} request", S::Protocol::DEBUG_NAME))
            .unwrap();

        Ok(())
    });

    fut.await.with_context(|| format!("error reading {} request", S::Protocol::DEBUG_NAME))?;

    Ok(())
}

/// Runs the server in the background and logs the error if one occurs.
///
/// This implements the most common case where FIDL service authors serve a
/// RequestStream on some remote task using `Task::spawn(...).detach()`.
///
/// When using this function, prefer prefixing your program's log messages by
/// annotating `main()` with one of the following:
///
/// ```
/// #[fuchsia::main(logging = true)]
/// #[fuchsia::main(logging_prefix = "my_prefix")]
/// ```
pub fn serve_detached<S, H>(stream: S, handler: H)
where
    S: RequestStream + 'static,
    H: RequestHandler<S::Protocol> + 'static,
{
    fasync::Task::spawn(async move {
        if let Err(err) = serve(stream, handler).await {
            error!("{:?}", err);
        }
    })
    .detach();
}

/// Runs the server in the background and logs the error if one occurs.
///
/// This implements the most common case where FIDL service authors serve a
/// RequestStream on some remote task using `Task::spawn(...).detach()`.
///
/// When using this function, prefer prefixing your program's log messages by
/// annotating `main()` with one of the following:
///
/// ```
/// #[fuchsia::main(logging = true)]
/// #[fuchsia::main(logging_prefix = "my_prefix")]
/// ```
pub fn serve_async_detached<S, H>(stream: S, handler: H)
where
    S: RequestStream + 'static,
    S::Ok: Send,
    H: AsyncRequestHandler<S::Protocol> + 'static,
{
    fasync::Task::spawn(async move {
        if let Err(err) = serve_async(stream, handler).await {
            error!("{:?}", err);
        }
    })
    .detach();
}