| // 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. | |
| use { | |
| crate::{ | |
| events::{ | |
| CapabilityRouted, Event, EventSource, EventStream, EventStreamError, RoutingProtocol, | |
| }, | |
| matcher::EventMatcher, | |
| }, | |
| anyhow::Error, | |
| async_trait::async_trait, | |
| fidl::endpoints::{create_request_stream, ClientEnd, ServerEnd, ServiceMarker}, | |
| fidl_fuchsia_io as fio, fidl_fuchsia_sys2 as fsys, fuchsia_async as fasync, | |
| futures::{ | |
| future::{AbortHandle, Abortable, TryFutureExt}, | |
| StreamExt, | |
| }, | |
| log::warn, | |
| std::sync::Arc, | |
| }; | |
| /// A ProtocolInterposer allows a test to sit between a service and a client | |
| /// and mutate or silently observe messages being passed between them. | |
| /// | |
| /// Client <---> ProtocolInterposer <---> Server | |
| #[async_trait] | |
| pub trait ProtocolInterposer: 'static + Send + Sync { | |
| type Marker: ServiceMarker; | |
| async fn interpose( | |
| self: Arc<Self>, | |
| event_source: &EventSource, | |
| matcher: EventMatcher, | |
| ) -> AbortHandle { | |
| let matcher = matcher.capability_id(Self::Marker::NAME); | |
| let server_end = event_source | |
| .subscribe_endpoint(vec![CapabilityRouted::NAME]) | |
| .await | |
| .expect("Could not subscribe to CapabilityRouted event for interposition"); | |
| let (abort_handle, abort_registration) = AbortHandle::new_pair(); | |
| // Spawn a new thread to listen to CapabilityRoutedEvents. | |
| // We use a new thread here because running this on the main thread may | |
| // not work if a test writer needs to do blocking operations. | |
| fasync::Task::blocking( | |
| Abortable::new( | |
| async move { | |
| let mut event_stream = EventStream::new( | |
| server_end | |
| .into_stream() | |
| .expect("Could not create EventStream from ServerEnd"), | |
| ); | |
| loop { | |
| // Wait for a capability routed event that matches | |
| let event = | |
| match matcher.clone().wait::<CapabilityRouted>(&mut event_stream).await | |
| { | |
| Ok(e) => e, | |
| Err(e) => match e.downcast::<EventStreamError>() { | |
| Ok(EventStreamError::StreamClosed) => return, | |
| Err(e) => panic!("Unknown error! {:?}", e), | |
| }, | |
| }; | |
| // An event was found! Inject the route. | |
| if event.result.is_ok() { | |
| let (provider_client_end, server_end) = self.clone().route(); | |
| event | |
| .protocol_proxy() | |
| .expect("Event does not have routing protocol") | |
| .replace_and_open(provider_client_end, server_end) | |
| .await | |
| .expect("Could not set provider for CapabilityRouted event"); | |
| } | |
| } | |
| }, | |
| abort_registration, | |
| ) | |
| .unwrap_or_else(|_| ()), | |
| ) | |
| .detach(); | |
| abort_handle | |
| } | |
| fn route(self: Arc<Self>) -> (ClientEnd<fsys::CapabilityProviderMarker>, fidl::Channel) { | |
| // Create the Interposer <---> Server channel | |
| let (client_end, server_end) = fidl::Channel::create().expect("could not create channel"); | |
| // Create the CapabilityProvider channel | |
| let (provider_client_end, mut provider_capability_stream) = | |
| create_request_stream::<fsys::CapabilityProviderMarker>() | |
| .expect("Could not create request stream for CapabilityProvider"); | |
| // Spawn a task to handle this new route | |
| fasync::Task::spawn(async move { | |
| if let Some(Ok(fsys::CapabilityProviderRequest::Open { | |
| server_end, | |
| flags, | |
| mode, | |
| path, | |
| responder, | |
| })) = provider_capability_stream.next().await | |
| { | |
| // Unblock component manager | |
| responder.send().expect("Failed to respond to CapabilityProvider Open"); | |
| if !path.is_empty() { | |
| warn!( | |
| "Interposed service {} was provided a non-empty path: {}", | |
| Self::Marker::NAME, | |
| path | |
| ); | |
| } | |
| if flags != (fio::OPEN_RIGHT_READABLE | fio::OPEN_RIGHT_WRITABLE) { | |
| warn!( | |
| "Interposed service {} was provided unexpected flags: 0x{:x}", | |
| Self::Marker::NAME, | |
| flags | |
| ); | |
| } | |
| let mode_type = mode & fio::MODE_TYPE_MASK; | |
| if mode_type != fio::MODE_TYPE_SERVICE { | |
| warn!( | |
| "Interposed service {} was provided unexpected mode type: 0x{:x}", | |
| Self::Marker::NAME, | |
| mode_type | |
| ); | |
| } | |
| // Create the proxy for the Interposer <---> Server connection | |
| let proxy = ClientEnd::<Self::Marker>::new(client_end) | |
| .into_proxy() | |
| .expect("could not convert into proxy"); | |
| // Create the stream for the Client <---> Interposer connection | |
| let stream = ServerEnd::<Self::Marker>::new(server_end) | |
| .into_stream() | |
| .expect("could not convert channel into stream"); | |
| // Start interposing! | |
| self.serve(stream, proxy).await.expect("Interposition failed"); | |
| } | |
| }) | |
| .detach(); | |
| (provider_client_end, server_end) | |
| } | |
| /// This function will be run asynchronously when a client attempts | |
| /// to connect to the service being interposed. `from_client` is a stream of | |
| /// requests coming in from the client and `to_server` is a proxy to the | |
| /// real server. | |
| async fn serve( | |
| self: Arc<Self>, | |
| mut from_client: <<Self as ProtocolInterposer>::Marker as ServiceMarker>::RequestStream, | |
| to_server: <<Self as ProtocolInterposer>::Marker as ServiceMarker>::Proxy, | |
| ) -> Result<(), Error>; | |
| } |