blob: e35b05940789e84b89a82e609838eba5f21786ef [file] [log] [blame]
// Copyright 2018 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.
//! Wrapper types for the endpoints of a connection.
use {
crate::{
epitaph::ChannelEpitaphExt, AsHandleRef, AsyncChannel, Channel, Error, Handle, HandleBased,
HandleRef, OnSignalsRef, ServeInner,
},
fuchsia_async as fasync, fuchsia_zircon_status as zx_status,
futures::{Future, FutureExt, Stream, TryFutureExt, TryStream, TryStreamExt},
std::marker::PhantomData,
std::sync::Arc,
tracing::error,
};
/// A marker for a particular FIDL protocol.
///
/// Implementations of this trait can be used to manufacture instances of a FIDL
/// protocol and get metadata about a particular protocol.
pub trait ProtocolMarker: Sized + Send + Sync + 'static {
/// The type of the structure against which FIDL requests are made.
/// Queries made against the proxy are sent to the paired `ServerEnd`.
type Proxy: Proxy<Protocol = Self>;
/// The type of the structure against which thread-blocking FIDL requests are made.
/// Queries made against the proxy are sent to the paired `ServerEnd`.
#[cfg(target_os = "fuchsia")]
type SynchronousProxy: SynchronousProxy<Protocol = Self>;
/// The type of the stream of requests coming into a server.
type RequestStream: RequestStream<Protocol = Self>;
/// The name of the protocol suitable for debug purposes.
///
/// For discoverable protocols, this should be identical to
/// `<Self as DiscoverableProtocolMarker>::PROTOCOL_NAME`.
const DEBUG_NAME: &'static str;
}
/// A marker for a particular FIDL protocol that is also discoverable.
///
/// Discoverable protocols may be referred to by a string name, and can be
/// conveniently exported in a service directory via an entry of that name.
///
/// If you get an error about this trait not being implemented, you probably
/// need to add the `@discoverable` attribute to the FIDL protocol, like this:
///
/// ```fidl
/// @discoverable
/// protocol MyProtocol { ... };
/// ```
pub trait DiscoverableProtocolMarker: ProtocolMarker {
/// The name of the protocol (to be used for service lookup and discovery).
const PROTOCOL_NAME: &'static str = <Self as ProtocolMarker>::DEBUG_NAME;
}
/// A type which allows querying a remote FIDL server over a channel.
pub trait Proxy: Sized + Send + Sync {
/// The protocol which this `Proxy` controls.
type Protocol: ProtocolMarker<Proxy = Self>;
/// Create a proxy over the given channel.
fn from_channel(inner: AsyncChannel) -> Self;
/// Attempt to convert the proxy back into a channel.
///
/// This will only succeed if there are no active clones of this proxy
/// and no currently-alive `EventStream` or response futures that came from
/// this proxy.
fn into_channel(self) -> Result<AsyncChannel, Self>;
/// Attempt to convert the proxy back into a client end.
///
/// This will only succeed if there are no active clones of this proxy
/// and no currently-alive `EventStream` or response futures that came from
/// this proxy.
fn into_client_end(self) -> Result<ClientEnd<Self::Protocol>, Self> {
match self.into_channel() {
Ok(channel) => Ok(ClientEnd::new(channel.into_zx_channel())),
Err(proxy) => Err(proxy),
}
}
/// Get a reference to the proxy's underlying channel.
///
/// This should only be used for non-effectful operations. Reading or
/// writing to the channel is unsafe because the proxy assumes it has
/// exclusive control over these operations.
fn as_channel(&self) -> &AsyncChannel;
/// Returns true if the proxy has received the `PEER_CLOSED` signal.
fn is_closed(&self) -> bool {
self.as_channel().is_closed()
}
/// Returns a future that completes when the proxy receives the
/// `PEER_CLOSED` signal.
fn on_closed(&self) -> OnSignalsRef<'_> {
self.as_channel().on_closed()
}
}
/// A type which allows querying a remote FIDL server over a channel, blocking the calling thread.
#[cfg(target_os = "fuchsia")]
pub trait SynchronousProxy: Sized + Send + Sync {
/// The async proxy for the same protocol.
type Proxy: Proxy<Protocol = Self::Protocol>;
/// The protocol which this `Proxy` controls.
type Protocol: ProtocolMarker<Proxy = Self::Proxy>;
/// Create a proxy over the given channel.
fn from_channel(inner: Channel) -> Self;
/// Convert the proxy back into a channel.
fn into_channel(self) -> Channel;
/// Get a reference to the proxy's underlying channel.
///
/// This should only be used for non-effectful operations. Reading or
/// writing to the channel is unsafe because the proxy assumes it has
/// exclusive control over these operations.
fn as_channel(&self) -> &Channel;
}
/// A stream of requests coming into a FIDL server over a channel.
pub trait RequestStream: Sized + Send + Stream + TryStream<Error = crate::Error> + Unpin {
/// The protocol which this `RequestStream` serves.
type Protocol: ProtocolMarker<RequestStream = Self>;
/// The control handle for this `RequestStream`.
type ControlHandle: ControlHandle;
/// Returns a copy of the `ControlHandle` for the given stream.
/// This handle can be used to send events or shut down the request stream.
fn control_handle(&self) -> Self::ControlHandle;
/// Create a request stream from the given channel.
fn from_channel(inner: AsyncChannel) -> Self;
/// Convert this channel into its underlying components.
fn into_inner(self) -> (Arc<ServeInner>, bool);
/// Create this channel from its underlying components.
fn from_inner(inner: Arc<ServeInner>, is_terminated: bool) -> Self;
/// Convert this FIDL request stream into a request stream of another FIDL protocol.
fn cast_stream<T: RequestStream>(self) -> T {
let inner = self.into_inner();
T::from_inner(inner.0, inner.1)
}
}
/// The Request type associated with a Marker.
pub type Request<Marker> = <<Marker as ProtocolMarker>::RequestStream as futures::TryStream>::Ok;
/// A type associated with a `RequestStream` that can be used to send FIDL
/// events or to shut down the request stream.
pub trait ControlHandle {
/// Set the server to shutdown. The underlying channel is only closed the
/// next time the stream is polled.
// TODO(https://fxbug.dev/42161447): Fix behavior or above docs.
fn shutdown(&self);
/// Sets the server to shutdown with an epitaph. The underlying channel is
/// only closed the next time the stream is polled.
// TODO(https://fxbug.dev/42161447): Fix behavior or above docs.
fn shutdown_with_epitaph(&self, status: zx_status::Status);
/// Returns true if the server has received the `PEER_CLOSED` signal.
fn is_closed(&self) -> bool;
/// Returns a future that completes when the server receives the
/// `PEER_CLOSED` signal.
fn on_closed(&self) -> OnSignalsRef<'_>;
}
/// A type associated with a particular two-way FIDL method, used by servers to
/// send a response to the client.
pub trait Responder {
/// The control handle for this protocol.
type ControlHandle: ControlHandle;
/// Returns the `ControlHandle` for this protocol.
fn control_handle(&self) -> &Self::ControlHandle;
/// Drops the responder without setting the channel to shutdown.
///
/// This method shouldn't normally be used. Instead, send a response to
/// prevent the channel from shutting down.
fn drop_without_shutdown(self);
}
/// A marker for a particular FIDL service.
#[cfg(target_os = "fuchsia")]
pub trait ServiceMarker: Sized + Send + Sync + 'static {
/// The type of the proxy object upon which calls are made to a remote FIDL service.
type Proxy: ServiceProxy<Service = Self>;
/// The request type for this particular FIDL service.
type Request: ServiceRequest<Service = Self>;
/// The name of the service. Used for service lookup and discovery.
const SERVICE_NAME: &'static str;
}
/// A request to initiate a connection to a FIDL service.
#[cfg(target_os = "fuchsia")]
pub trait ServiceRequest: Sized + Send + Sync {
/// The FIDL service for which this request is destined.
type Service: ServiceMarker<Request = Self>;
/// Dispatches a connection attempt to this FIDL service's member protocol
/// identified by `name`, producing an instance of this trait.
fn dispatch(name: &str, channel: AsyncChannel) -> Self;
/// Returns an array of the service members' names.
fn member_names() -> &'static [&'static str];
}
/// Proxy by which a client sends messages to a FIDL service.
#[cfg(target_os = "fuchsia")]
pub trait ServiceProxy: Sized {
/// The FIDL service this proxy represents.
type Service: ServiceMarker<Proxy = Self>;
/// Create a proxy from a MemberOpener implementation.
#[doc(hidden)]
fn from_member_opener(opener: Box<dyn MemberOpener>) -> Self;
}
/// Used to create an indirection between the fuchsia.io.Directory protocol
/// and this library, which cannot depend on fuchsia.io.
#[doc(hidden)]
#[cfg(target_os = "fuchsia")]
pub trait MemberOpener {
/// Opens a member protocol of a FIDL service by name, serving that protocol
/// on the given channel.
fn open_member(&self, member: &str, server_end: Channel) -> Result<(), Error>;
}
/// Utility that spawns a new task to handle requests of a particular type, requiring a
/// singlethreaded executor. The requests are handled one at a time.
// TODO(https://fxbug.dev/319159026) this should be infallible
pub fn spawn_local_stream_handler<P, F, Fut>(f: F) -> Result<P, Error>
where
P: Proxy,
F: FnMut(Request<P::Protocol>) -> Fut + 'static,
Fut: Future<Output = ()> + 'static,
{
let (proxy, stream) = create_proxy_and_stream::<P::Protocol>()?;
fasync::Task::local(for_each_or_log(stream, f)).detach();
Ok(proxy)
}
/// Utility that spawns a new task to handle requests of a particular type. The request handler
/// must be threadsafe. The requests are handled one at a time.
// TODO(https://fxbug.dev/319159026) this should be infallible
pub fn spawn_stream_handler<P, F, Fut>(f: F) -> Result<P, Error>
where
P: Proxy,
F: FnMut(Request<P::Protocol>) -> Fut + 'static + Send,
Fut: Future<Output = ()> + 'static + Send,
{
let (proxy, stream) = create_proxy_and_stream::<P::Protocol>()?;
fasync::Task::spawn(for_each_or_log(stream, f)).detach();
Ok(proxy)
}
fn for_each_or_log<St, F, Fut>(stream: St, mut f: F) -> impl Future<Output = ()>
where
St: RequestStream,
F: FnMut(St::Ok) -> Fut,
Fut: Future<Output = ()>,
{
stream
.try_for_each(move |r| f(r).map(Ok))
.unwrap_or_else(|e| error!("FIDL stream handler failed: {}", e))
}
/// The `Client` end of a FIDL connection.
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct ClientEnd<T> {
inner: Channel,
phantom: PhantomData<T>,
}
impl<T> ClientEnd<T> {
/// Create a new client from the provided channel.
pub fn new(inner: Channel) -> Self {
ClientEnd { inner, phantom: PhantomData }
}
/// Get a reference to the underlying channel
pub fn channel(&self) -> &Channel {
&self.inner
}
/// Extract the underlying channel.
pub fn into_channel(self) -> Channel {
self.inner
}
}
impl<T: ProtocolMarker> ClientEnd<T> {
/// Convert the `ClientEnd` into a `Proxy` through which FIDL calls may be made.
// TODO(https://fxbug.dev/319159026) this should be infallible
pub fn into_proxy(self) -> Result<T::Proxy, Error> {
Ok(T::Proxy::from_channel(AsyncChannel::from_channel(self.inner)))
}
/// Soft-transition affordance for https://fxbug.dev/319159026.
// TODO(https://fxbug.dev/319159026) delete this function
pub fn try_into_proxy(self) -> Result<T::Proxy, Error> {
self.into_proxy()
}
/// Convert the `ClientEnd` into a `SynchronousProxy` through which thread-blocking FIDL calls
/// may be made.
#[cfg(target_os = "fuchsia")]
pub fn into_sync_proxy(self) -> T::SynchronousProxy {
T::SynchronousProxy::from_channel(self.inner)
}
}
impl<T> AsHandleRef for ClientEnd<T> {
fn as_handle_ref(&self) -> HandleRef<'_> {
self.inner.as_handle_ref()
}
}
impl<T> From<ClientEnd<T>> for Handle {
fn from(client: ClientEnd<T>) -> Handle {
client.into_channel().into()
}
}
impl<T> From<ClientEnd<T>> for Channel {
fn from(client: ClientEnd<T>) -> Channel {
client.into_channel()
}
}
impl<T> From<Handle> for ClientEnd<T> {
fn from(handle: Handle) -> Self {
ClientEnd { inner: handle.into(), phantom: PhantomData }
}
}
impl<T> From<Channel> for ClientEnd<T> {
fn from(chan: Channel) -> Self {
ClientEnd { inner: chan, phantom: PhantomData }
}
}
impl<T: ProtocolMarker> ::std::fmt::Debug for ClientEnd<T> {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(f, "ClientEnd(name={}, channel={:?})", T::DEBUG_NAME, self.inner)
}
}
impl<T> HandleBased for ClientEnd<T> {}
/// The `Server` end of a FIDL connection.
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct ServerEnd<T> {
inner: Channel,
phantom: PhantomData<T>,
}
impl<T> ServerEnd<T> {
/// Create a new `ServerEnd` from the provided channel.
pub fn new(inner: Channel) -> ServerEnd<T> {
ServerEnd { inner, phantom: PhantomData }
}
/// Get a reference to the underlying channel
pub fn channel(&self) -> &Channel {
&self.inner
}
/// Extract the inner channel.
pub fn into_channel(self) -> Channel {
self.inner
}
/// Create a stream of requests off of the channel.
///
/// # Panics
///
/// If called outside the context of an active async executor.
// TODO(https://fxbug.dev/319159026) this should be infallible
pub fn into_stream(self) -> Result<T::RequestStream, Error>
where
T: ProtocolMarker,
{
Ok(T::RequestStream::from_channel(AsyncChannel::from_channel(self.inner)))
}
/// Soft-transition affordance for https://fxbug.dev/319159026.
// TODO(https://fxbug.dev/319159026) delete this function
pub fn try_into_stream(self) -> Result<T::RequestStream, Error>
where
T: ProtocolMarker,
{
self.into_stream()
}
/// Create a stream of requests and an event-sending handle
/// from the channel.
///
/// # Panics
///
/// If called outside the context of an active async executor.
// TODO(https://fxbug.dev/319159026) this should be infallible
pub fn into_stream_and_control_handle(
self,
) -> Result<(T::RequestStream, <T::RequestStream as RequestStream>::ControlHandle), Error>
where
T: ProtocolMarker,
{
let stream = self.into_stream()?;
let control_handle = stream.control_handle();
Ok((stream, control_handle))
}
/// Writes an epitaph into the underlying channel before closing it.
pub fn close_with_epitaph(self, status: zx_status::Status) -> Result<(), Error> {
self.inner.close_with_epitaph(status)
}
}
impl<T> AsHandleRef for ServerEnd<T> {
fn as_handle_ref(&self) -> HandleRef<'_> {
self.inner.as_handle_ref()
}
}
impl<T> From<ServerEnd<T>> for Handle {
fn from(server: ServerEnd<T>) -> Handle {
server.into_channel().into()
}
}
impl<T> From<ServerEnd<T>> for Channel {
fn from(server: ServerEnd<T>) -> Channel {
server.into_channel()
}
}
impl<T> From<Handle> for ServerEnd<T> {
fn from(handle: Handle) -> Self {
ServerEnd { inner: handle.into(), phantom: PhantomData }
}
}
impl<T> From<Channel> for ServerEnd<T> {
fn from(chan: Channel) -> Self {
ServerEnd { inner: chan, phantom: PhantomData }
}
}
impl<T: ProtocolMarker> ::std::fmt::Debug for ServerEnd<T> {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(f, "ServerEnd(name={}, channel={:?})", T::DEBUG_NAME, self.inner)
}
}
impl<T> HandleBased for ServerEnd<T> {}
/// Creates client and server endpoints connected to by a channel.
pub fn create_endpoints<T: ProtocolMarker>() -> (ClientEnd<T>, ServerEnd<T>) {
let (client, server) = Channel::create();
let client_end = ClientEnd::<T>::new(client);
let server_end = ServerEnd::new(server);
(client_end, server_end)
}
/// Create a client proxy and a server endpoint connected to it by a channel.
///
/// Useful for sending channel handles to calls that take arguments
/// of type `server_end:SomeProtocol`
///
/// # Panics
///
/// If called outside the context of an active async executor.
// TODO(https://fxbug.dev/319159026) this should be infallible
pub fn create_proxy<T: ProtocolMarker>() -> Result<(T::Proxy, ServerEnd<T>), Error> {
let (client, server) = create_endpoints();
Ok((client.into_proxy()?, server))
}
/// Soft-transition affordance for https://fxbug.dev/319159026.
// TODO(https://fxbug.dev/319159026) delete this function
pub fn try_create_proxy<T: ProtocolMarker>() -> Result<(T::Proxy, ServerEnd<T>), Error> {
create_proxy::<T>()
}
/// Create a synchronous client proxy and a server endpoint connected to it by a channel.
///
/// Useful for sending channel handles to calls that take arguments
/// of type `server_end:SomeProtocol`
#[cfg(target_os = "fuchsia")]
pub fn create_sync_proxy<T: ProtocolMarker>() -> (T::SynchronousProxy, ServerEnd<T>) {
let (client, server) = create_endpoints();
(client.into_sync_proxy(), server)
}
/// Create a request stream and a client endpoint connected to it by a channel.
///
/// Useful for sending channel handles to calls that take arguments
/// of type `client_end:SomeProtocol`
///
/// # Panics
///
/// If called outside the context of an active async executor.
// TODO(https://fxbug.dev/319159026) this should be infallible
pub fn create_request_stream<T: ProtocolMarker>() -> Result<(ClientEnd<T>, T::RequestStream), Error>
{
let (client, server) = create_endpoints();
Ok((client, server.into_stream()?))
}
/// Soft-transition affordance for https://fxbug.dev/319159026.
// TODO(https://fxbug.dev/319159026) delete this function
pub fn try_create_request_stream<T: ProtocolMarker>(
) -> Result<(ClientEnd<T>, T::RequestStream), Error> {
create_request_stream::<T>()
}
/// Create a request stream and proxy connected to one another.
///
/// Useful for testing where both the request stream and proxy are
/// used in the same process.
///
/// # Panics
///
/// If called outside the context of an active async executor.
// TODO(https://fxbug.dev/319159026) this should be infallible
pub fn create_proxy_and_stream<T: ProtocolMarker>() -> Result<(T::Proxy, T::RequestStream), Error> {
let (client, server) = create_endpoints::<T>();
Ok((client.into_proxy()?, server.into_stream()?))
}
/// Soft-transition affordance for https://fxbug.dev/319159026.
// TODO(https://fxbug.dev/319159026) delete this function
pub fn try_create_proxy_and_stream<T: ProtocolMarker>(
) -> Result<(T::Proxy, T::RequestStream), Error> {
create_proxy_and_stream::<T>()
}
/// Create a request stream and synchronous proxy connected to one another.
///
/// Useful for testing where both the request stream and proxy are
/// used in the same process.
///
/// # Panics
///
/// If called outside the context of an active async executor.
// TODO(https://fxbug.dev/319159026) this should be infallible
#[cfg(target_os = "fuchsia")]
pub fn create_sync_proxy_and_stream<T: ProtocolMarker>(
) -> Result<(T::SynchronousProxy, T::RequestStream), Error> {
let (client, server) = create_endpoints::<T>();
Ok((client.into_sync_proxy(), server.into_stream()?))
}
/// The type of a client-initiated method.
#[derive(Copy, Clone, Debug)]
pub enum MethodType {
/// One-way method, also known as fire-and-forget.
OneWay,
/// Two-way method.
TwoWay,
}