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