Skip to main content

fidl/
endpoints.rs

1// Copyright 2018 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
5//! Wrapper types for the endpoints of a connection.
6
7use crate::epitaph::ChannelEpitaphExt;
8use crate::{
9    AsHandleRef, AsyncChannel, Channel, Error, HandleBased, HandleRef, NullableHandle,
10    OnSignalsRef, ServeInner,
11};
12use futures::{Stream, TryStream};
13use std::marker::PhantomData;
14use std::sync::Arc;
15
16/// A marker for a particular FIDL protocol.
17///
18/// Implementations of this trait can be used to manufacture instances of a FIDL
19/// protocol and get metadata about a particular protocol.
20pub trait ProtocolMarker: Sized + Send + Sync + 'static {
21    /// The type of the structure against which FIDL requests are made.
22    /// Queries made against the proxy are sent to the paired `ServerEnd`.
23    type Proxy: Proxy<Protocol = Self>;
24
25    /// The type of the structure against which thread-blocking FIDL requests are made.
26    /// Queries made against the proxy are sent to the paired `ServerEnd`.
27    #[cfg(target_os = "fuchsia")]
28    type SynchronousProxy: SynchronousProxy<Protocol = Self>;
29
30    /// The type of the stream of requests coming into a server.
31    type RequestStream: RequestStream<Protocol = Self>;
32
33    /// The name of the protocol suitable for debug purposes.
34    ///
35    /// For discoverable protocols, this should be identical to
36    /// `<Self as DiscoverableProtocolMarker>::PROTOCOL_NAME`.
37    const DEBUG_NAME: &'static str;
38}
39
40/// A marker for a particular FIDL protocol that is also discoverable.
41///
42/// Discoverable protocols may be referred to by a string name, and can be
43/// conveniently exported in a service directory via an entry of that name.
44///
45/// If you get an error about this trait not being implemented, you probably
46/// need to add the `@discoverable` attribute to the FIDL protocol, like this:
47///
48/// ```fidl
49/// @discoverable
50/// protocol MyProtocol { ... };
51/// ```
52pub trait DiscoverableProtocolMarker: ProtocolMarker {
53    /// The name of the protocol (to be used for service lookup and discovery).
54    const PROTOCOL_NAME: &'static str = <Self as ProtocolMarker>::DEBUG_NAME;
55}
56
57/// A type which allows querying a remote FIDL server over a channel.
58pub trait Proxy: Sized + Send + Sync {
59    /// The protocol which this `Proxy` controls.
60    type Protocol: ProtocolMarker<Proxy = Self>;
61
62    /// Create a proxy over the given channel.
63    fn from_channel(inner: AsyncChannel) -> Self;
64
65    /// Attempt to convert the proxy back into a channel.
66    ///
67    /// This will only succeed if there are no active clones of this proxy
68    /// and no currently-alive `EventStream` or response futures that came from
69    /// this proxy.
70    fn into_channel(self) -> Result<AsyncChannel, Self>;
71
72    /// Attempt to convert the proxy back into a client end.
73    ///
74    /// This will only succeed if there are no active clones of this proxy
75    /// and no currently-alive `EventStream` or response futures that came from
76    /// this proxy.
77    fn into_client_end(self) -> Result<ClientEnd<Self::Protocol>, Self> {
78        match self.into_channel() {
79            Ok(channel) => Ok(ClientEnd::new(channel.into_zx_channel())),
80            Err(proxy) => Err(proxy),
81        }
82    }
83
84    /// Get a reference to the proxy's underlying channel.
85    ///
86    /// This should only be used for non-effectful operations. Reading or
87    /// writing to the channel is unsafe because the proxy assumes it has
88    /// exclusive control over these operations.
89    fn as_channel(&self) -> &AsyncChannel;
90
91    /// Returns true if the proxy has received the `PEER_CLOSED` signal.
92    fn is_closed(&self) -> bool {
93        self.as_channel().is_closed()
94    }
95
96    /// Returns a future that completes when the proxy receives the
97    /// `PEER_CLOSED` signal.
98    fn on_closed(&self) -> OnSignalsRef<'_> {
99        self.as_channel().on_closed()
100    }
101}
102
103/// This gives native Zircon proxies a domain method like FDomain proxies have.
104/// This makes it easier in some cases to build the same code for both FDomain
105/// and regular FIDL.
106pub trait ProxyHasDomain {
107    /// Get a "client" for this proxy. This is just an object which has methods
108    /// for a few common handle creation operations.
109    fn domain(&self) -> ZirconClient {
110        ZirconClient
111    }
112}
113
114impl<T: Proxy> ProxyHasDomain for T {}
115
116/// The fake "client" produced by `ProxyHasDomain`. Analogous to an FDomain client.
117pub struct ZirconClient;
118
119impl ZirconClient {
120    /// Equivalent to [`EventPair::create`]
121    pub fn create_event_pair(&self) -> (crate::EventPair, crate::EventPair) {
122        crate::EventPair::create()
123    }
124
125    /// Equivalent to [`Event::create`]
126    pub fn create_event(&self) -> crate::Event {
127        crate::Event::create()
128    }
129
130    /// Equivalent to [`Socket::create_stream`]
131    pub fn create_stream_socket(&self) -> (crate::Socket, crate::Socket) {
132        crate::Socket::create_stream()
133    }
134
135    /// Equivalent to [`Socket::create_datagram`]
136    pub fn create_datagram_socket(&self) -> (crate::Socket, crate::Socket) {
137        crate::Socket::create_datagram()
138    }
139
140    /// Equivalent to [`Channel::create`]
141    pub fn create_channel(&self) -> (Channel, Channel) {
142        Channel::create()
143    }
144
145    /// Equivalent to the module level [`create_endpoints`]
146    pub fn create_endpoints<T: ProtocolMarker>(&self) -> (ClientEnd<T>, ServerEnd<T>) {
147        create_endpoints::<T>()
148    }
149
150    /// Equivalent to the module level [`create_proxy`]
151    pub fn create_proxy<T: ProtocolMarker>(&self) -> (T::Proxy, ServerEnd<T>) {
152        create_proxy::<T>()
153    }
154
155    /// Equivalent to the module level [`create_request_stream`]
156    pub fn create_request_stream<T: ProtocolMarker>(&self) -> (ClientEnd<T>, T::RequestStream) {
157        create_request_stream::<T>()
158    }
159
160    /// Equivalent to the module level [`create_proxy_and_stream`]
161    pub fn create_proxy_and_stream<T: ProtocolMarker>(&self) -> (T::Proxy, T::RequestStream) {
162        create_proxy_and_stream::<T>()
163    }
164}
165
166/// A type which allows querying a remote FIDL server over a channel, blocking the calling thread.
167#[cfg(target_os = "fuchsia")]
168pub trait SynchronousProxy: Sized + Send + Sync {
169    /// The async proxy for the same protocol.
170    type Proxy: Proxy<Protocol = Self::Protocol>;
171
172    /// The protocol which this `Proxy` controls.
173    type Protocol: ProtocolMarker<Proxy = Self::Proxy>;
174
175    /// Create a proxy over the given channel.
176    fn from_channel(inner: Channel) -> Self;
177
178    /// Convert the proxy back into a channel.
179    fn into_channel(self) -> Channel;
180
181    /// Get a reference to the proxy's underlying channel.
182    ///
183    /// This should only be used for non-effectful operations. Reading or
184    /// writing to the channel is unsafe because the proxy assumes it has
185    /// exclusive control over these operations.
186    fn as_channel(&self) -> &Channel;
187
188    /// Returns true if the proxy has received the `PEER_CLOSED` signal.
189    ///
190    /// # Errors
191    ///
192    /// See https://fuchsia.dev/reference/syscalls/object_wait_one?hl=en#errors for a full list of
193    /// errors. Note that `Status::TIMED_OUT` errors are converted to `Ok(false)` and all other
194    /// errors are propagated.
195    fn is_closed(&self) -> Result<bool, zx::Status> {
196        use zx::Peered;
197        self.as_channel().is_closed()
198    }
199}
200
201/// A stream of requests coming into a FIDL server over a channel.
202pub trait RequestStream: Sized + Send + Stream + TryStream<Error = crate::Error> + Unpin {
203    /// The protocol which this `RequestStream` serves.
204    type Protocol: ProtocolMarker<RequestStream = Self>;
205
206    /// The control handle for this `RequestStream`.
207    type ControlHandle: ControlHandle;
208
209    /// Returns a copy of the `ControlHandle` for the given stream.
210    /// This handle can be used to send events or shut down the request stream.
211    fn control_handle(&self) -> Self::ControlHandle;
212
213    /// Create a request stream from the given channel.
214    fn from_channel(inner: AsyncChannel) -> Self;
215
216    /// Convert this channel into its underlying components.
217    fn into_inner(self) -> (Arc<ServeInner>, bool);
218
219    /// Create this channel from its underlying components.
220    fn from_inner(inner: Arc<ServeInner>, is_terminated: bool) -> Self;
221
222    /// Convert this FIDL request stream into a request stream of another FIDL protocol.
223    fn cast_stream<T: RequestStream>(self) -> T {
224        let inner = self.into_inner();
225        T::from_inner(inner.0, inner.1)
226    }
227}
228
229/// The Request type associated with a Marker.
230pub type Request<Marker> = <<Marker as ProtocolMarker>::RequestStream as futures::TryStream>::Ok;
231
232/// A type associated with a `RequestStream` that can be used to send FIDL
233/// events or to shut down the request stream.
234pub trait ControlHandle {
235    /// Set the server to shutdown. The underlying channel is only closed the
236    /// next time the stream is polled.
237    // TODO(https://fxbug.dev/42161447): Fix behavior or above docs.
238    fn shutdown(&self);
239
240    /// Sets the server to shutdown with an epitaph. The underlying channel is
241    /// only closed the next time the stream is polled.
242    // TODO(https://fxbug.dev/42161447): Fix behavior or above docs.
243    fn shutdown_with_epitaph(&self, status: zx_status::Status);
244
245    /// Returns true if the server has received the `PEER_CLOSED` signal.
246    fn is_closed(&self) -> bool;
247
248    /// Returns a future that completes when the server receives the
249    /// `PEER_CLOSED` signal.
250    fn on_closed(&self) -> OnSignalsRef<'_>;
251
252    /// Sets and clears the signals provided on peer handle.
253    #[cfg(target_os = "fuchsia")]
254    fn signal_peer(
255        &self,
256        clear_mask: zx::Signals,
257        set_mask: zx::Signals,
258    ) -> Result<(), zx_status::Status>;
259}
260
261/// A type associated with a particular two-way FIDL method, used by servers to
262/// send a response to the client.
263pub trait Responder {
264    /// The control handle for this protocol.
265    type ControlHandle: ControlHandle;
266
267    /// Returns the `ControlHandle` for this protocol.
268    fn control_handle(&self) -> &Self::ControlHandle;
269
270    /// Drops the responder without setting the channel to shutdown.
271    ///
272    /// This method shouldn't normally be used. Instead, send a response to
273    /// prevent the channel from shutting down.
274    fn drop_without_shutdown(self);
275}
276
277/// A marker for a particular FIDL service.
278#[cfg(target_os = "fuchsia")]
279pub trait ServiceMarker: Clone + Sized + Send + Sync + 'static {
280    /// The type of the proxy object upon which calls are made to a remote FIDL service.
281    type Proxy: ServiceProxy<Service = Self>;
282
283    /// The request type for this particular FIDL service.
284    type Request: ServiceRequest<Service = Self>;
285
286    /// The name of the service. Used for service lookup and discovery.
287    const SERVICE_NAME: &'static str;
288}
289
290/// A request to initiate a connection to a FIDL service.
291#[cfg(target_os = "fuchsia")]
292pub trait ServiceRequest: Sized + Send + Sync {
293    /// The FIDL service for which this request is destined.
294    type Service: ServiceMarker<Request = Self>;
295
296    /// Dispatches a connection attempt to this FIDL service's member protocol
297    /// identified by `name`, producing an instance of this trait.
298    fn dispatch(name: &str, channel: AsyncChannel) -> Self;
299
300    /// Returns an array of the service members' names.
301    fn member_names() -> &'static [&'static str];
302}
303
304/// Proxy by which a client sends messages to a FIDL service.
305#[cfg(target_os = "fuchsia")]
306pub trait ServiceProxy: Sized {
307    /// The FIDL service this proxy represents.
308    type Service: ServiceMarker<Proxy = Self>;
309
310    /// Create a proxy from a MemberOpener implementation.
311    #[doc(hidden)]
312    fn from_member_opener(opener: Box<dyn MemberOpener>) -> Self;
313}
314
315/// Used to create an indirection between the fuchsia.io.Directory protocol
316/// and this library, which cannot depend on fuchsia.io.
317#[doc(hidden)]
318#[cfg(target_os = "fuchsia")]
319pub trait MemberOpener: Send + Sync {
320    /// Opens a member protocol of a FIDL service by name, serving that protocol
321    /// on the given channel.
322    fn open_member(&self, member: &str, server_end: Channel) -> Result<(), Error>;
323
324    /// Returns the name of the instance that was opened.
325    fn instance_name(&self) -> &str;
326}
327
328/// The `Client` end of a FIDL connection.
329#[derive(Eq, PartialEq, Ord, PartialOrd, Hash)]
330pub struct ClientEnd<T> {
331    inner: Channel,
332    phantom: PhantomData<T>,
333}
334
335impl<T> ClientEnd<T> {
336    /// Create a new client from the provided channel.
337    pub fn new(inner: Channel) -> Self {
338        ClientEnd { inner, phantom: PhantomData }
339    }
340
341    /// Get a reference to the underlying channel
342    pub fn channel(&self) -> &Channel {
343        &self.inner
344    }
345
346    /// Extract the underlying channel.
347    pub fn into_channel(self) -> Channel {
348        self.inner
349    }
350}
351
352impl<T: ProtocolMarker> ClientEnd<T> {
353    /// Convert the `ClientEnd` into a `Proxy` through which FIDL calls may be made.
354    ///
355    /// # Panics
356    ///
357    /// If called outside the context of an active async executor.
358    pub fn into_proxy(self) -> T::Proxy {
359        T::Proxy::from_channel(AsyncChannel::from_channel(self.inner))
360    }
361
362    /// Convert the `ClientEnd` into a `SynchronousProxy` through which thread-blocking FIDL calls
363    /// may be made.
364    #[cfg(target_os = "fuchsia")]
365    pub fn into_sync_proxy(self) -> T::SynchronousProxy {
366        T::SynchronousProxy::from_channel(self.inner)
367    }
368}
369
370impl<T> AsHandleRef for ClientEnd<T> {
371    fn as_handle_ref(&self) -> HandleRef<'_> {
372        self.inner.as_handle_ref()
373    }
374}
375
376impl<T> From<ClientEnd<T>> for NullableHandle {
377    fn from(client: ClientEnd<T>) -> NullableHandle {
378        client.into_channel().into()
379    }
380}
381
382impl<T> From<ClientEnd<T>> for Channel {
383    fn from(client: ClientEnd<T>) -> Channel {
384        client.into_channel()
385    }
386}
387
388impl<T> From<NullableHandle> for ClientEnd<T> {
389    fn from(handle: NullableHandle) -> Self {
390        ClientEnd { inner: handle.into(), phantom: PhantomData }
391    }
392}
393
394impl<T> From<Channel> for ClientEnd<T> {
395    fn from(chan: Channel) -> Self {
396        ClientEnd { inner: chan, phantom: PhantomData }
397    }
398}
399
400impl<T: ProtocolMarker> ::std::fmt::Debug for ClientEnd<T> {
401    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
402        write!(f, "ClientEnd(name={}, channel={:?})", T::DEBUG_NAME, self.inner)
403    }
404}
405
406impl<T> HandleBased for ClientEnd<T> {}
407
408/// Trait implemented by types that can be converted from a client.
409pub trait FromClient {
410    /// The protocol.
411    type Protocol: ProtocolMarker;
412
413    /// Converts from a client.
414    fn from_client(value: ClientEnd<Self::Protocol>) -> Self;
415}
416
417impl<T: ProtocolMarker> FromClient for ClientEnd<T> {
418    type Protocol = T;
419
420    fn from_client(value: ClientEnd<Self::Protocol>) -> Self {
421        value
422    }
423}
424
425// NOTE: We can only have one blanket implementation. Synchronous proxies have an implementation
426// that is generated by the compiler.
427impl<T: Proxy> FromClient for T {
428    type Protocol = T::Protocol;
429
430    fn from_client(value: ClientEnd<Self::Protocol>) -> Self {
431        Self::from_channel(AsyncChannel::from_channel(value.into_channel()))
432    }
433}
434
435/// The `Server` end of a FIDL connection.
436#[derive(Eq, PartialEq, Ord, PartialOrd, Hash)]
437pub struct ServerEnd<T> {
438    inner: Channel,
439    phantom: PhantomData<T>,
440}
441
442impl<T> ServerEnd<T> {
443    /// Create a new `ServerEnd` from the provided channel.
444    pub fn new(inner: Channel) -> ServerEnd<T> {
445        ServerEnd { inner, phantom: PhantomData }
446    }
447
448    /// Get a reference to the underlying channel
449    pub fn channel(&self) -> &Channel {
450        &self.inner
451    }
452
453    /// Extract the inner channel.
454    pub fn into_channel(self) -> Channel {
455        self.inner
456    }
457
458    /// Create a stream of requests off of the channel.
459    ///
460    /// # Panics
461    ///
462    /// If called outside the context of an active async executor.
463    pub fn into_stream(self) -> T::RequestStream
464    where
465        T: ProtocolMarker,
466    {
467        T::RequestStream::from_channel(AsyncChannel::from_channel(self.inner))
468    }
469
470    /// Create a stream of requests and an event-sending handle
471    /// from the channel.
472    ///
473    /// # Panics
474    ///
475    /// If called outside the context of an active async executor.
476    pub fn into_stream_and_control_handle(
477        self,
478    ) -> (T::RequestStream, <T::RequestStream as RequestStream>::ControlHandle)
479    where
480        T: ProtocolMarker,
481    {
482        let stream = self.into_stream();
483        let control_handle = stream.control_handle();
484        (stream, control_handle)
485    }
486
487    /// Writes an epitaph into the underlying channel before closing it.
488    pub fn close_with_epitaph(self, status: zx_status::Status) -> Result<(), Error> {
489        self.inner.close_with_epitaph(status)
490    }
491}
492
493impl<T> AsHandleRef for ServerEnd<T> {
494    fn as_handle_ref(&self) -> HandleRef<'_> {
495        self.inner.as_handle_ref()
496    }
497}
498
499impl<T> From<ServerEnd<T>> for NullableHandle {
500    fn from(server: ServerEnd<T>) -> NullableHandle {
501        server.into_channel().into()
502    }
503}
504
505impl<T> From<ServerEnd<T>> for Channel {
506    fn from(server: ServerEnd<T>) -> Channel {
507        server.into_channel()
508    }
509}
510
511impl<T> From<NullableHandle> for ServerEnd<T> {
512    fn from(handle: NullableHandle) -> Self {
513        ServerEnd { inner: handle.into(), phantom: PhantomData }
514    }
515}
516
517impl<T> From<Channel> for ServerEnd<T> {
518    fn from(chan: Channel) -> Self {
519        ServerEnd { inner: chan, phantom: PhantomData }
520    }
521}
522
523impl<T: ProtocolMarker> ::std::fmt::Debug for ServerEnd<T> {
524    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
525        write!(f, "ServerEnd(name={}, channel={:?})", T::DEBUG_NAME, self.inner)
526    }
527}
528
529impl<T> HandleBased for ServerEnd<T> {}
530
531/// Creates client and server endpoints connected to by a channel.
532pub fn create_endpoints<T: ProtocolMarker>() -> (ClientEnd<T>, ServerEnd<T>) {
533    let (client, server) = Channel::create();
534    let client_end = ClientEnd::<T>::new(client);
535    let server_end = ServerEnd::new(server);
536    (client_end, server_end)
537}
538
539/// Create a client proxy and a server endpoint connected to it by a channel.
540///
541/// Useful for sending channel handles to calls that take arguments
542/// of type `server_end:SomeProtocol`
543///
544/// # Panics
545///
546/// If called outside the context of an active async executor.
547pub fn create_proxy<T: ProtocolMarker>() -> (T::Proxy, ServerEnd<T>) {
548    let (client, server) = create_endpoints();
549    (client.into_proxy(), server)
550}
551
552/// Create a synchronous client proxy and a server endpoint connected to it by a channel.
553///
554/// Useful for sending channel handles to calls that take arguments
555/// of type `server_end:SomeProtocol`
556#[cfg(target_os = "fuchsia")]
557pub fn create_sync_proxy<T: ProtocolMarker>() -> (T::SynchronousProxy, ServerEnd<T>) {
558    let (client, server) = create_endpoints();
559    (client.into_sync_proxy(), server)
560}
561
562/// Create a request stream and a client endpoint connected to it by a channel.
563///
564/// Useful for sending channel handles to calls that take arguments
565/// of type `client_end:SomeProtocol`
566///
567/// # Panics
568///
569/// If called outside the context of an active async executor.
570pub fn create_request_stream<T: ProtocolMarker>() -> (ClientEnd<T>, T::RequestStream) {
571    let (client, server) = create_endpoints();
572    (client, server.into_stream())
573}
574
575/// Create a request stream and proxy connected to one another.
576///
577/// Useful for testing where both the request stream and proxy are
578/// used in the same process.
579///
580/// # Panics
581///
582/// If called outside the context of an active async executor.
583pub fn create_proxy_and_stream<T: ProtocolMarker>() -> (T::Proxy, T::RequestStream) {
584    let (client, server) = create_endpoints::<T>();
585    (client.into_proxy(), server.into_stream())
586}
587
588/// Create a request stream and synchronous proxy connected to one another.
589///
590/// Useful for testing where both the request stream and proxy are
591/// used in the same process.
592///
593/// # Panics
594///
595/// If called outside the context of an active async executor.
596#[cfg(target_os = "fuchsia")]
597pub fn create_sync_proxy_and_stream<T: ProtocolMarker>() -> (T::SynchronousProxy, T::RequestStream)
598{
599    let (client, server) = create_endpoints::<T>();
600    (client.into_sync_proxy(), server.into_stream())
601}
602
603/// The type of a client-initiated method.
604#[derive(Copy, Clone, Debug)]
605pub enum MethodType {
606    /// One-way method, also known as fire-and-forget.
607    OneWay,
608    /// Two-way method.
609    TwoWay,
610}