blob: 3ec8900d2d00e85e5951ebc6ad5ea33b2c7de160 [file] [log] [blame]
// Copyright 2023 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.
use {
anyhow::{Error, Result},
fidl::endpoints,
fidl_fidl_examples_routing_echo as fecho, fidl_fuchsia_component_sandbox as fsandbox,
fidl_test_echoserver::{RealmFactoryRequest, RealmFactoryRequestStream, RealmOptions},
fuchsia_async as fasync,
fuchsia_component::{client, server::ServiceFs},
fuchsia_component_test::{Capability, ChildOptions, RealmBuilder, RealmInstance, Ref, Route},
futures::{StreamExt, TryStreamExt},
tracing::*,
};
#[fuchsia::main]
async fn main() -> Result<(), Error> {
let mut fs = ServiceFs::new();
fs.dir("svc").add_fidl_service(|stream: RealmFactoryRequestStream| stream);
fs.take_and_serve_directory_handle()?;
fs.for_each_concurrent(0, serve_realm_factory).await;
Ok(())
}
async fn serve_realm_factory(mut stream: RealmFactoryRequestStream) {
let mut task_group = fasync::TaskGroup::new();
let result: Result<(), Error> = async move {
while let Ok(Some(request)) = stream.try_next().await {
match request {
RealmFactoryRequest::CreateRealm { options, dictionary, responder } => {
let realm = create_realm(options).await?;
// Get a dict containing the capabilities exposed by the realm.
let (expose_dict, server_end) = endpoints::create_proxy().unwrap();
let (my_dictionary_proxy, server) = endpoints::create_proxy().unwrap();
realm.root.controller().get_exposed_dictionary(server_end).await?.unwrap();
let () = expose_dict.copy(server)?;
// Mix in additional capabilities to the dict.
//
// TODO(https://fxbug.dev/298100106): Could RealmInstance expose a higher-level
// API for getting the realm's exposed dict, along with APIs that make it
// easy to add in more capabilities served by this component? For example:
//
// let mut bundle = realm.get_root_bundle().extend().await?.unwrap();
// let echo_request_stream =
// bundles::add_fidl_service::<fecho::EchoMarker>(&bundle);
// "serves" the bundle over `bundle_server` which was sent from the client.
// Also moves `bundle` so it can't be modified further.
// bundle.serve(bundle_server).await?.unwrap();
//
// ... code to serve echo here ...
let (echo_sender_client, echo_sender_server) =
endpoints::create_endpoints::<fsandbox::SenderMarker>();
let (echo_receiver_client, echo_receiver_stream) =
endpoints::create_request_stream::<fsandbox::ReceiverMarker>()?;
let factory = client::connect_to_protocol::<fsandbox::FactoryMarker>()?;
let () =
factory.create_sender(echo_receiver_client, echo_sender_server).await?;
my_dictionary_proxy
.insert("reverse-echo", fsandbox::Capability::Sender(echo_sender_client))
.await?
.unwrap();
// Bind the dict.
my_dictionary_proxy.clone2(dictionary.into_channel().into())?;
// Serve the mixed-in capability.
task_group.spawn(async move {
let _realm = realm;
let _ = realm_proxy::service::handle_receiver::<fecho::EchoMarker, _, _>(
echo_receiver_stream,
handle_echo_request_stream,
)
.await
.map_err(|e| {
error!("Failed to serve echo stream: {}", e);
});
});
responder.send(Ok(()))?;
}
RealmFactoryRequest::_UnknownMethod { .. } => unimplemented!(),
}
}
task_group.join().await;
Ok(())
}
.await;
if let Err(err) = result {
error!("{:?}", err);
}
}
async fn create_realm(options: RealmOptions) -> Result<RealmInstance, Error> {
info!("building the realm using options {:?}", options);
let builder = RealmBuilder::new().await?;
let echo =
builder.add_child("echo", "echo_server#meta/default.cm", ChildOptions::new()).await?;
builder
.add_route(
Route::new()
.capability(Capability::protocol::<fecho::EchoMarker>())
.from(&echo)
.to(Ref::parent()),
)
.await?;
let realm = builder.build().await?;
Ok(realm)
}
async fn handle_echo_request_stream(mut stream: fecho::EchoRequestStream) {
while let Ok(Some(request)) = stream.try_next().await {
match request {
fecho::EchoRequest::EchoString { value, responder } => {
let value = value.map(|value| {
let mut reversed = String::with_capacity(value.len());
let mut chars: Vec<_> = value.chars().collect();
chars.reverse();
for ch in chars {
reversed.push(ch);
}
reversed
});
responder.send(value.as_deref()).unwrap();
}
}
}
}