blob: beb4969ea7f6fa426211176dd1b937ee3eb73cd2 [file] [log] [blame]
// Copyright 2019 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.
//! An implementation of a server for a fidl interface.
use {
crate::{
encoding::{
DynamicFlags, EmptyStruct, Encode, Encoder, Flexible, FlexibleType, FrameworkErr,
TransactionHeader, TransactionMessage, TransactionMessageType, TypeMarker,
},
epitaph,
handle::HandleDisposition,
AsyncChannel, Error, HandleInfo,
},
fuchsia_zircon_status as zx_status,
futures::task::{AtomicWaker, Context},
std::sync::atomic::{self, AtomicBool},
};
/// A type used from the innards of server implementations.
#[derive(Debug)]
pub struct ServeInner {
waker: AtomicWaker,
shutdown: AtomicBool,
channel: AsyncChannel,
}
impl ServeInner {
/// Creates a new set of server innards.
pub fn new(channel: AsyncChannel) -> Self {
let waker = AtomicWaker::new();
let shutdown = AtomicBool::new(false);
ServeInner { waker, shutdown, channel }
}
/// Gets a reference to the inner channel.
pub fn channel(&self) -> &AsyncChannel {
&self.channel
}
/// Converts the [`ServerInner`] back into a channel.
///
/// **Warning**: This operation is dangerous, since the returned channel
/// could have unread messages intended for this server. Use it carefully.
pub fn into_channel(self) -> AsyncChannel {
self.channel
}
/// Sets the server to shutdown the next time the stream is polled.
///
/// TODO(https://fxbug.dev/42153903): This should cause the channel to close on the
/// next poll, but in fact the channel won't close until the user of the
/// bindings drops their request stream, responders, and control handles.
pub fn shutdown(&self) {
self.shutdown.store(true, atomic::Ordering::Relaxed);
self.waker.wake();
}
/// Sets the server to shutdown with an epitaph the next time the stream is polled.
///
/// TODO(https://fxbug.dev/42153903): This should cause the channel to close on the
/// next poll, but in fact the channel won't close until the user of the
/// bindings drops their request stream, responders, and control handles.
pub fn shutdown_with_epitaph(&self, status: zx_status::Status) {
let already_shutting_down = self.shutdown.swap(true, atomic::Ordering::Relaxed);
if !already_shutting_down {
// Ignore the error, best effort sending an epitaph.
let _ = epitaph::write_epitaph_impl(&self.channel, status);
self.waker.wake();
}
}
/// Checks if the server has been set to shutdown.
pub fn check_shutdown(&self, cx: &Context<'_>) -> bool {
if self.shutdown.load(atomic::Ordering::Relaxed) {
return true;
}
self.waker.register(cx.waker());
self.shutdown.load(atomic::Ordering::Relaxed)
}
/// Sends an encodable message to the client.
#[inline]
pub fn send<T: TypeMarker>(
&self,
body: impl Encode<T>,
tx_id: u32,
ordinal: u64,
dynamic_flags: DynamicFlags,
) -> Result<(), Error> {
let msg = TransactionMessage {
header: TransactionHeader::new(tx_id, ordinal, dynamic_flags),
body,
};
crate::encoding::with_tls_encoded::<TransactionMessageType<T>, ()>(msg, |bytes, handles| {
self.send_raw_msg(bytes, handles)
})
}
/// Sends a framework error to the client.
///
/// The caller must be inside a `with_tls_decode_buf` callback, and pass the
/// buffers used to decode the request as the `tls_decode_buf` parameter.
#[inline]
pub fn send_framework_err(
&self,
framework_err: FrameworkErr,
tx_id: u32,
ordinal: u64,
dynamic_flags: DynamicFlags,
tls_decode_buf: (&mut Vec<u8>, &mut Vec<HandleInfo>),
) -> Result<(), Error> {
type Msg = TransactionMessageType<FlexibleType<EmptyStruct>>;
let msg = TransactionMessage {
header: TransactionHeader::new(tx_id, ordinal, dynamic_flags),
body: Flexible::<()>::FrameworkErr(framework_err),
};
// RFC-0138 requires us to close handles in the incoming message before replying.
let (bytes, handle_infos) = tls_decode_buf;
handle_infos.clear();
// Reuse the request decoding byte buffer for encoding (we can't call
// `with_tls_encoded` as we're already inside `with_tls_decode_buf`).
let mut handle_dispositions = Vec::new();
Encoder::encode::<Msg>(bytes, &mut handle_dispositions, msg)?;
debug_assert!(handle_dispositions.is_empty());
self.send_raw_msg(&*bytes, &mut [])
}
/// Sends a raw message to the client.
fn send_raw_msg(
&self,
bytes: &[u8],
handles: &mut [HandleDisposition<'_>],
) -> Result<(), Error> {
match self.channel.write_etc(bytes, handles) {
Ok(()) | Err(zx_status::Status::PEER_CLOSED) => Ok(()),
Err(e) => Err(Error::ServerResponseWrite(e)),
}
}
}