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_proxy_and_stream`]
156 pub fn create_proxy_and_stream<T: ProtocolMarker>(&self) -> (T::Proxy, T::RequestStream) {
157 create_proxy_and_stream::<T>()
158 }
159}
160
161/// A type which allows querying a remote FIDL server over a channel, blocking the calling thread.
162#[cfg(target_os = "fuchsia")]
163pub trait SynchronousProxy: Sized + Send + Sync {
164 /// The async proxy for the same protocol.
165 type Proxy: Proxy<Protocol = Self::Protocol>;
166
167 /// The protocol which this `Proxy` controls.
168 type Protocol: ProtocolMarker<Proxy = Self::Proxy>;
169
170 /// Create a proxy over the given channel.
171 fn from_channel(inner: Channel) -> Self;
172
173 /// Convert the proxy back into a channel.
174 fn into_channel(self) -> Channel;
175
176 /// Get a reference to the proxy's underlying channel.
177 ///
178 /// This should only be used for non-effectful operations. Reading or
179 /// writing to the channel is unsafe because the proxy assumes it has
180 /// exclusive control over these operations.
181 fn as_channel(&self) -> &Channel;
182
183 /// Returns true if the proxy has received the `PEER_CLOSED` signal.
184 ///
185 /// # Errors
186 ///
187 /// See https://fuchsia.dev/reference/syscalls/object_wait_one?hl=en#errors for a full list of
188 /// errors. Note that `Status::TIMED_OUT` errors are converted to `Ok(false)` and all other
189 /// errors are propagated.
190 fn is_closed(&self) -> Result<bool, zx::Status> {
191 use zx::Peered;
192 self.as_channel().is_closed()
193 }
194}
195
196/// A stream of requests coming into a FIDL server over a channel.
197pub trait RequestStream: Sized + Send + Stream + TryStream<Error = crate::Error> + Unpin {
198 /// The protocol which this `RequestStream` serves.
199 type Protocol: ProtocolMarker<RequestStream = Self>;
200
201 /// The control handle for this `RequestStream`.
202 type ControlHandle: ControlHandle;
203
204 /// Returns a copy of the `ControlHandle` for the given stream.
205 /// This handle can be used to send events or shut down the request stream.
206 fn control_handle(&self) -> Self::ControlHandle;
207
208 /// Create a request stream from the given channel.
209 fn from_channel(inner: AsyncChannel) -> Self;
210
211 /// Convert this channel into its underlying components.
212 fn into_inner(self) -> (Arc<ServeInner>, bool);
213
214 /// Create this channel from its underlying components.
215 fn from_inner(inner: Arc<ServeInner>, is_terminated: bool) -> Self;
216
217 /// Convert this FIDL request stream into a request stream of another FIDL protocol.
218 fn cast_stream<T: RequestStream>(self) -> T {
219 let inner = self.into_inner();
220 T::from_inner(inner.0, inner.1)
221 }
222}
223
224/// The Request type associated with a Marker.
225pub type Request<Marker> = <<Marker as ProtocolMarker>::RequestStream as futures::TryStream>::Ok;
226
227/// A type associated with a `RequestStream` that can be used to send FIDL
228/// events or to shut down the request stream.
229pub trait ControlHandle {
230 /// Set the server to shutdown. The underlying channel is only closed the
231 /// next time the stream is polled.
232 // TODO(https://fxbug.dev/42161447): Fix behavior or above docs.
233 fn shutdown(&self);
234
235 /// Sets the server to shutdown with an epitaph. The underlying channel is
236 /// only closed the next time the stream is polled.
237 // TODO(https://fxbug.dev/42161447): Fix behavior or above docs.
238 fn shutdown_with_epitaph(&self, status: zx_status::Status);
239
240 /// Returns true if the server has received the `PEER_CLOSED` signal.
241 fn is_closed(&self) -> bool;
242
243 /// Returns a future that completes when the server receives the
244 /// `PEER_CLOSED` signal.
245 fn on_closed(&self) -> OnSignalsRef<'_>;
246
247 /// Sets and clears the signals provided on peer handle.
248 #[cfg(target_os = "fuchsia")]
249 fn signal_peer(
250 &self,
251 clear_mask: zx::Signals,
252 set_mask: zx::Signals,
253 ) -> Result<(), zx_status::Status>;
254}
255
256/// A type associated with a particular two-way FIDL method, used by servers to
257/// send a response to the client.
258pub trait Responder {
259 /// The control handle for this protocol.
260 type ControlHandle: ControlHandle;
261
262 /// Returns the `ControlHandle` for this protocol.
263 fn control_handle(&self) -> &Self::ControlHandle;
264
265 /// Drops the responder without setting the channel to shutdown.
266 ///
267 /// This method shouldn't normally be used. Instead, send a response to
268 /// prevent the channel from shutting down.
269 fn drop_without_shutdown(self);
270}
271
272/// A marker for a particular FIDL service.
273#[cfg(target_os = "fuchsia")]
274pub trait ServiceMarker: Clone + Sized + Send + Sync + 'static {
275 /// The type of the proxy object upon which calls are made to a remote FIDL service.
276 type Proxy: ServiceProxy<Service = Self>;
277
278 /// The request type for this particular FIDL service.
279 type Request: ServiceRequest<Service = Self>;
280
281 /// The name of the service. Used for service lookup and discovery.
282 const SERVICE_NAME: &'static str;
283}
284
285/// A request to initiate a connection to a FIDL service.
286#[cfg(target_os = "fuchsia")]
287pub trait ServiceRequest: Sized + Send + Sync {
288 /// The FIDL service for which this request is destined.
289 type Service: ServiceMarker<Request = Self>;
290
291 /// Dispatches a connection attempt to this FIDL service's member protocol
292 /// identified by `name`, producing an instance of this trait.
293 fn dispatch(name: &str, channel: AsyncChannel) -> Self;
294
295 /// Returns an array of the service members' names.
296 fn member_names() -> &'static [&'static str];
297}
298
299/// Proxy by which a client sends messages to a FIDL service.
300#[cfg(target_os = "fuchsia")]
301pub trait ServiceProxy: Sized {
302 /// The FIDL service this proxy represents.
303 type Service: ServiceMarker<Proxy = Self>;
304
305 /// Create a proxy from a MemberOpener implementation.
306 #[doc(hidden)]
307 fn from_member_opener(opener: Box<dyn MemberOpener>) -> Self;
308}
309
310/// Used to create an indirection between the fuchsia.io.Directory protocol
311/// and this library, which cannot depend on fuchsia.io.
312#[doc(hidden)]
313#[cfg(target_os = "fuchsia")]
314pub trait MemberOpener: Send + Sync {
315 /// Opens a member protocol of a FIDL service by name, serving that protocol
316 /// on the given channel.
317 fn open_member(&self, member: &str, server_end: Channel) -> Result<(), Error>;
318
319 /// Returns the name of the instance that was opened.
320 fn instance_name(&self) -> &str;
321}
322
323/// The `Client` end of a FIDL connection.
324#[derive(Eq, PartialEq, Ord, PartialOrd, Hash)]
325pub struct ClientEnd<T> {
326 inner: Channel,
327 phantom: PhantomData<T>,
328}
329
330impl<T> ClientEnd<T> {
331 /// Create a new client from the provided channel.
332 pub fn new(inner: Channel) -> Self {
333 ClientEnd { inner, phantom: PhantomData }
334 }
335
336 /// Get a reference to the underlying channel
337 pub fn channel(&self) -> &Channel {
338 &self.inner
339 }
340
341 /// Extract the underlying channel.
342 pub fn into_channel(self) -> Channel {
343 self.inner
344 }
345}
346
347impl<T: ProtocolMarker> ClientEnd<T> {
348 /// Convert the `ClientEnd` into a `Proxy` through which FIDL calls may be made.
349 ///
350 /// # Panics
351 ///
352 /// If called outside the context of an active async executor.
353 pub fn into_proxy(self) -> T::Proxy {
354 T::Proxy::from_channel(AsyncChannel::from_channel(self.inner))
355 }
356
357 /// Convert the `ClientEnd` into a `SynchronousProxy` through which thread-blocking FIDL calls
358 /// may be made.
359 #[cfg(target_os = "fuchsia")]
360 pub fn into_sync_proxy(self) -> T::SynchronousProxy {
361 T::SynchronousProxy::from_channel(self.inner)
362 }
363}
364
365impl<T> AsHandleRef for ClientEnd<T> {
366 fn as_handle_ref(&self) -> HandleRef<'_> {
367 self.inner.as_handle_ref()
368 }
369}
370
371impl<T> From<ClientEnd<T>> for NullableHandle {
372 fn from(client: ClientEnd<T>) -> NullableHandle {
373 client.into_channel().into()
374 }
375}
376
377impl<T> From<ClientEnd<T>> for Channel {
378 fn from(client: ClientEnd<T>) -> Channel {
379 client.into_channel()
380 }
381}
382
383impl<T> From<NullableHandle> for ClientEnd<T> {
384 fn from(handle: NullableHandle) -> Self {
385 ClientEnd { inner: handle.into(), phantom: PhantomData }
386 }
387}
388
389impl<T> From<Channel> for ClientEnd<T> {
390 fn from(chan: Channel) -> Self {
391 ClientEnd { inner: chan, phantom: PhantomData }
392 }
393}
394
395impl<T: ProtocolMarker> ::std::fmt::Debug for ClientEnd<T> {
396 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
397 write!(f, "ClientEnd(name={}, channel={:?})", T::DEBUG_NAME, self.inner)
398 }
399}
400
401impl<T> HandleBased for ClientEnd<T> {}
402
403/// Trait implemented by types that can be converted from a client.
404pub trait FromClient {
405 /// The protocol.
406 type Protocol: ProtocolMarker;
407
408 /// Converts from a client.
409 fn from_client(value: ClientEnd<Self::Protocol>) -> Self;
410}
411
412impl<T: ProtocolMarker> FromClient for ClientEnd<T> {
413 type Protocol = T;
414
415 fn from_client(value: ClientEnd<Self::Protocol>) -> Self {
416 value
417 }
418}
419
420// NOTE: We can only have one blanket implementation. Synchronous proxies have an implementation
421// that is generated by the compiler.
422impl<T: Proxy> FromClient for T {
423 type Protocol = T::Protocol;
424
425 fn from_client(value: ClientEnd<Self::Protocol>) -> Self {
426 Self::from_channel(AsyncChannel::from_channel(value.into_channel()))
427 }
428}
429
430/// The `Server` end of a FIDL connection.
431#[derive(Eq, PartialEq, Ord, PartialOrd, Hash)]
432pub struct ServerEnd<T> {
433 inner: Channel,
434 phantom: PhantomData<T>,
435}
436
437impl<T> ServerEnd<T> {
438 /// Create a new `ServerEnd` from the provided channel.
439 pub fn new(inner: Channel) -> ServerEnd<T> {
440 ServerEnd { inner, phantom: PhantomData }
441 }
442
443 /// Get a reference to the underlying channel
444 pub fn channel(&self) -> &Channel {
445 &self.inner
446 }
447
448 /// Extract the inner channel.
449 pub fn into_channel(self) -> Channel {
450 self.inner
451 }
452
453 /// Create a stream of requests off of the channel.
454 ///
455 /// # Panics
456 ///
457 /// If called outside the context of an active async executor.
458 pub fn into_stream(self) -> T::RequestStream
459 where
460 T: ProtocolMarker,
461 {
462 T::RequestStream::from_channel(AsyncChannel::from_channel(self.inner))
463 }
464
465 /// Create a stream of requests and an event-sending handle
466 /// from the channel.
467 ///
468 /// # Panics
469 ///
470 /// If called outside the context of an active async executor.
471 pub fn into_stream_and_control_handle(
472 self,
473 ) -> (T::RequestStream, <T::RequestStream as RequestStream>::ControlHandle)
474 where
475 T: ProtocolMarker,
476 {
477 let stream = self.into_stream();
478 let control_handle = stream.control_handle();
479 (stream, control_handle)
480 }
481
482 /// Writes an epitaph into the underlying channel before closing it.
483 pub fn close_with_epitaph(self, status: zx_status::Status) -> Result<(), Error> {
484 self.inner.close_with_epitaph(status)
485 }
486}
487
488impl<T> AsHandleRef for ServerEnd<T> {
489 fn as_handle_ref(&self) -> HandleRef<'_> {
490 self.inner.as_handle_ref()
491 }
492}
493
494impl<T> From<ServerEnd<T>> for NullableHandle {
495 fn from(server: ServerEnd<T>) -> NullableHandle {
496 server.into_channel().into()
497 }
498}
499
500impl<T> From<ServerEnd<T>> for Channel {
501 fn from(server: ServerEnd<T>) -> Channel {
502 server.into_channel()
503 }
504}
505
506impl<T> From<NullableHandle> for ServerEnd<T> {
507 fn from(handle: NullableHandle) -> Self {
508 ServerEnd { inner: handle.into(), phantom: PhantomData }
509 }
510}
511
512impl<T> From<Channel> for ServerEnd<T> {
513 fn from(chan: Channel) -> Self {
514 ServerEnd { inner: chan, phantom: PhantomData }
515 }
516}
517
518impl<T: ProtocolMarker> ::std::fmt::Debug for ServerEnd<T> {
519 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
520 write!(f, "ServerEnd(name={}, channel={:?})", T::DEBUG_NAME, self.inner)
521 }
522}
523
524impl<T> HandleBased for ServerEnd<T> {}
525
526/// Creates client and server endpoints connected to by a channel.
527pub fn create_endpoints<T: ProtocolMarker>() -> (ClientEnd<T>, ServerEnd<T>) {
528 let (client, server) = Channel::create();
529 let client_end = ClientEnd::<T>::new(client);
530 let server_end = ServerEnd::new(server);
531 (client_end, server_end)
532}
533
534/// Create a client proxy and a server endpoint connected to it by a channel.
535///
536/// Useful for sending channel handles to calls that take arguments
537/// of type `server_end:SomeProtocol`
538///
539/// # Panics
540///
541/// If called outside the context of an active async executor.
542pub fn create_proxy<T: ProtocolMarker>() -> (T::Proxy, ServerEnd<T>) {
543 let (client, server) = create_endpoints();
544 (client.into_proxy(), server)
545}
546
547/// Create a synchronous client proxy and a server endpoint connected to it by a channel.
548///
549/// Useful for sending channel handles to calls that take arguments
550/// of type `server_end:SomeProtocol`
551#[cfg(target_os = "fuchsia")]
552pub fn create_sync_proxy<T: ProtocolMarker>() -> (T::SynchronousProxy, ServerEnd<T>) {
553 let (client, server) = create_endpoints();
554 (client.into_sync_proxy(), server)
555}
556
557/// Create a request stream and a client endpoint connected to it by a channel.
558///
559/// Useful for sending channel handles to calls that take arguments
560/// of type `client_end:SomeProtocol`
561///
562/// # Panics
563///
564/// If called outside the context of an active async executor.
565pub fn create_request_stream<T: ProtocolMarker>() -> (ClientEnd<T>, T::RequestStream) {
566 let (client, server) = create_endpoints();
567 (client, server.into_stream())
568}
569
570/// Create a request stream and proxy connected to one another.
571///
572/// Useful for testing where both the request stream and proxy are
573/// used in the same process.
574///
575/// # Panics
576///
577/// If called outside the context of an active async executor.
578pub fn create_proxy_and_stream<T: ProtocolMarker>() -> (T::Proxy, T::RequestStream) {
579 let (client, server) = create_endpoints::<T>();
580 (client.into_proxy(), server.into_stream())
581}
582
583/// Create a request stream and synchronous proxy connected to one another.
584///
585/// Useful for testing where both the request stream and proxy are
586/// used in the same process.
587///
588/// # Panics
589///
590/// If called outside the context of an active async executor.
591#[cfg(target_os = "fuchsia")]
592pub fn create_sync_proxy_and_stream<T: ProtocolMarker>() -> (T::SynchronousProxy, T::RequestStream)
593{
594 let (client, server) = create_endpoints::<T>();
595 (client.into_sync_proxy(), server.into_stream())
596}
597
598/// The type of a client-initiated method.
599#[derive(Copy, Clone, Debug)]
600pub enum MethodType {
601 /// One-way method, also known as fire-and-forget.
602 OneWay,
603 /// Two-way method.
604 TwoWay,
605}