fdomain_client/
responder.rs

1// Copyright 2024 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.
4
5use crate::{Error, ordinals};
6use fidl_fuchsia_fdomain as proto;
7use futures::channel::oneshot::Sender;
8
9/// Sum type over oneshot senders which carry the responses to FDomain FIDL
10/// requests back to the requesting code.
11///
12/// A couple of the variants also carry the HID the original operation was
13/// performed on. This is used when we get a write error in response to those
14/// operations and need to clear it by sending an AcknowledgeWriteError message.
15pub(crate) enum Responder {
16    Namespace(Sender<Result<(), Error>>),
17    CreateChannel(Sender<Result<(), Error>>),
18    CreateSocket(Sender<Result<(), Error>>),
19    CreateEventPair(Sender<Result<(), Error>>),
20    CreateEvent(Sender<Result<(), Error>>),
21    SetSocketDisposition(Sender<Result<(), Error>>),
22    WriteSocket(Sender<Result<proto::SocketWriteSocketResponse, Error>>),
23    WriteChannel(Sender<Result<(), Error>>),
24    Close(Sender<Result<(), Error>>),
25    Duplicate(Sender<Result<(), Error>>),
26    Replace(Sender<Result<(), Error>>),
27    Signal(Sender<Result<(), Error>>),
28    SignalPeer(Sender<Result<(), Error>>),
29    WaitForSignals(Sender<Result<proto::FDomainWaitForSignalsResponse, Error>>),
30    GetKoid(Sender<Result<proto::FDomainGetKoidResponse, Error>>),
31
32    // Read channel/socket is a little different. We just need the handle ID as we're
33    // going to ask the client to handle the transaction for us.
34    ReadChannel(proto::HandleId),
35    ReadSocket(proto::HandleId),
36
37    // We always use the Ignore variant for these, but implementation is here
38    // for posterity.
39    _ReadChannelStreamingStart(Sender<Result<(), Error>>),
40    _ReadChannelStreamingStop(Sender<Result<(), Error>>),
41    _ReadSocketStreamingStart(Sender<Result<(), Error>>),
42    _ReadSocketStreamingStop(Sender<Result<(), Error>>),
43
44    /// Used when we want to ignore the reply to a request.
45    Ignore,
46}
47
48impl Responder {
49    /// Feed this responder a still-encoded FIDL request.
50    pub(crate) fn handle(
51        self,
52        client_inner: &mut crate::ClientInner,
53        result: Result<(fidl_message::TransactionHeader, &[u8]), crate::InnerError>,
54    ) -> fidl::Result<()> {
55        match self {
56            Responder::Namespace(sender) => {
57                Responder::dispatch_handle("namespace", ordinals::GET_NAMESPACE, sender, result)
58            }
59            Responder::CreateChannel(sender) => Responder::dispatch_handle(
60                "create_channel",
61                ordinals::CREATE_CHANNEL,
62                sender,
63                result,
64            ),
65            Responder::CreateSocket(sender) => {
66                Responder::dispatch_handle("create_socket", ordinals::CREATE_SOCKET, sender, result)
67            }
68            Responder::CreateEventPair(sender) => Responder::dispatch_handle(
69                "create_event_pair",
70                ordinals::CREATE_EVENT_PAIR,
71                sender,
72                result,
73            ),
74            Responder::CreateEvent(sender) => {
75                Responder::dispatch_handle("create_event", ordinals::CREATE_EVENT, sender, result)
76            }
77            Responder::SetSocketDisposition(sender) => Responder::dispatch_handle(
78                "set_socket_disposition",
79                ordinals::SET_SOCKET_DISPOSITION,
80                sender,
81                result,
82            ),
83            Responder::ReadSocket(id) => {
84                Responder::dispatch_handle_etc::<proto::SocketData, proto::Error>(
85                    "read_channel",
86                    ordinals::READ_SOCKET,
87                    move |msg| {
88                        client_inner.handle_socket_read_response(msg, id);
89                    },
90                    result,
91                )
92            }
93            Responder::ReadChannel(id) => Responder::dispatch_handle_etc::<_, proto::Error>(
94                "read_channel",
95                ordinals::READ_CHANNEL,
96                move |msg| {
97                    client_inner.handle_channel_read_response(msg, id);
98                },
99                result,
100            ),
101            Responder::WriteSocket(sender) => {
102                Responder::dispatch_handle_etc::<_, proto::WriteSocketError>(
103                    "write_socket",
104                    ordinals::WRITE_SOCKET,
105                    move |m| {
106                        let _ = sender.send(m);
107                    },
108                    result,
109                )
110            }
111            Responder::WriteChannel(sender) => {
112                Responder::dispatch_handle_etc::<_, proto::WriteChannelError>(
113                    "write_channel",
114                    ordinals::WRITE_CHANNEL,
115                    move |m| {
116                        let _ = sender.send(m);
117                    },
118                    result,
119                )
120            }
121            Responder::WaitForSignals(sender) => Responder::dispatch_handle(
122                "wait_for_signals",
123                ordinals::WAIT_FOR_SIGNALS,
124                sender,
125                result,
126            ),
127            Responder::Close(sender) => {
128                Responder::dispatch_handle("close", ordinals::CLOSE, sender, result)
129            }
130            Responder::Duplicate(sender) => {
131                Responder::dispatch_handle("duplicate", ordinals::DUPLICATE, sender, result)
132            }
133            Responder::Replace(sender) => {
134                Responder::dispatch_handle("replace", ordinals::REPLACE, sender, result)
135            }
136            Responder::Signal(sender) => {
137                Responder::dispatch_handle("signal", ordinals::SIGNAL, sender, result)
138            }
139            Responder::SignalPeer(sender) => {
140                Responder::dispatch_handle("signal_peer", ordinals::SIGNAL_PEER, sender, result)
141            }
142            Responder::GetKoid(sender) => {
143                Responder::dispatch_handle("get_koid", ordinals::GET_KOID, sender, result)
144            }
145            Responder::_ReadChannelStreamingStart(sender) => Responder::dispatch_handle(
146                "read_channel_streaming_start",
147                ordinals::READ_CHANNEL_STREAMING_START,
148                sender,
149                result,
150            ),
151            Responder::_ReadChannelStreamingStop(sender) => Responder::dispatch_handle(
152                "read_channel_streaming_stop",
153                ordinals::READ_CHANNEL_STREAMING_STOP,
154                sender,
155                result,
156            ),
157            Responder::_ReadSocketStreamingStart(sender) => Responder::dispatch_handle(
158                "read_socket_streaming_start",
159                ordinals::READ_SOCKET_STREAMING_START,
160                sender,
161                result,
162            ),
163            Responder::_ReadSocketStreamingStop(sender) => Responder::dispatch_handle(
164                "read_socket_streaming_stop",
165                ordinals::READ_SOCKET_STREAMING_STOP,
166                sender,
167                result,
168            ),
169            Responder::Ignore => Ok(()),
170        }
171    }
172
173    /// Complete the `handle` method for a `Responder`. Does not take the
174    /// responder itself; when this is called the responder has been unwrapped,
175    /// and the type arguments to this method encode what was learned from the
176    /// variant.
177    fn dispatch_handle<R: fidl_message::Body>(
178        method_name: &'static str,
179        ordinal: u64,
180        sender: Sender<Result<R, Error>>,
181        result: Result<(fidl_message::TransactionHeader, &[u8]), crate::InnerError>,
182    ) -> fidl::Result<()> {
183        Self::dispatch_handle_etc::<R, proto::Error>(
184            method_name,
185            ordinal,
186            move |m| {
187                let _ = sender.send(m);
188            },
189            result,
190        )
191    }
192
193    /// Same as `dispatch_handle` except the error type is generic, whereas it
194    /// may only be `proto::Error` for `dispatch_handle`.
195    fn dispatch_handle_etc<R: fidl_message::Body, S: Into<Error> + fidl_message::ErrorType>(
196        method_name: &'static str,
197        ordinal: u64,
198        send_fn: impl FnOnce(Result<R, Error>),
199        result: Result<(fidl_message::TransactionHeader, &[u8]), crate::InnerError>,
200    ) -> fidl::Result<()> {
201        match result {
202            Ok((header, body)) => {
203                if header.ordinal != ordinal {
204                    return Err(fidl::Error::InvalidResponseTxid);
205                }
206                let (res, ret) =
207                    match fidl_message::decode_response_flexible_result::<R, S>(header, body) {
208                        Ok(fidl_message::MaybeUnknown::Known(x)) => (x.map_err(Into::into), Ok(())),
209                        Ok(fidl_message::MaybeUnknown::Unknown) => {
210                            (Err(Error::Protocol(fidl::Error::UnsupportedMethod {
211                            method_name,
212                            protocol_name:
213                            <proto::FDomainMarker as fidl::endpoints::ProtocolMarker>::DEBUG_NAME
214                        })), Ok(()))
215                        }
216                        Err(e) => (Err(Error::Protocol(e.clone())), Err(e)),
217                    };
218                send_fn(res);
219                ret
220            }
221            Err(e) => {
222                send_fn(Err(e.into()));
223                Ok(())
224            }
225        }
226    }
227}