Skip to main content

mock_omaha_server_fuchsia/
lib.rs

1// Copyright 2026 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
5use anyhow::Error;
6use fuchsia_async as fasync;
7use futures::stream::StreamExt as _;
8use hyper_util::rt::TokioIo;
9use std::future::Future;
10use std::net::{Ipv4Addr, SocketAddr};
11use std::sync::{Arc, Mutex};
12
13pub use mock_omaha_server::*;
14
15/// An [`Executor`] implementation that spawns detached tasks on `fuchsia_async`.
16#[derive(Clone, Copy, Debug, Default)]
17pub struct FuchsiaExecutor;
18
19impl Executor for FuchsiaExecutor {
20    fn spawn(&self, fut: impl Future<Output = ()> + Send + 'static) {
21        fasync::Task::spawn(fut).detach();
22    }
23}
24
25pub struct FuchsiaListener {
26    addr: SocketAddr,
27    stream: fasync::net::AcceptStream,
28}
29
30impl FuchsiaListener {
31    pub fn bind(addr: &SocketAddr) -> Result<Self, std::io::Error> {
32        let listener = fasync::net::TcpListener::bind(addr)?;
33        let addr = listener.local_addr()?;
34        let stream = listener.accept_stream();
35        Ok(Self { addr, stream })
36    }
37}
38
39impl Listener for FuchsiaListener {
40    type Io = TokioIo<fuchsia_hyper::TcpStream>;
41    type Error = std::io::Error;
42
43    async fn accept(&mut self) -> Result<Self::Io, Self::Error> {
44        let (conn, _) = self.stream.next().await.ok_or_else(|| {
45            std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "stream closed")
46        })??;
47        Ok(TokioIo::new(fuchsia_hyper::TcpStream { stream: conn }))
48    }
49}
50
51pub trait OmahaServerExt {
52    fn start_and_detach(
53        arc_server: Arc<Mutex<OmahaServer>>,
54        addr: Option<SocketAddr>,
55    ) -> impl Future<Output = Result<String, Error>>;
56}
57
58impl OmahaServerExt for OmahaServer {
59    async fn start_and_detach(
60        arc_server: Arc<Mutex<OmahaServer>>,
61        addr: Option<SocketAddr>,
62    ) -> Result<String, Error> {
63        let addr = addr.unwrap_or_else(|| SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 0));
64        let listener = FuchsiaListener::bind(&addr)?;
65        let local_addr = listener.addr;
66        fasync::Task::spawn(async move {
67            let _ = OmahaServer::start(arc_server, listener, FuchsiaExecutor).await;
68        })
69        .detach();
70        Ok(format!("http://{local_addr}/"))
71    }
72}
73
74#[cfg(test)]
75mod unit_tests {
76    use super::*;
77
78    mock_omaha_server::declare_tests! {
79        test_attr: #[fasync::run_singlethreaded(test)],
80        start_server: async |server| <OmahaServer as OmahaServerExt>::start_and_detach(server, None).await,
81        new_http_client: async || fuchsia_hyper::new_client(),
82        cup_expect_panic: "mock-omaha-server was configured to expect CUP, but we received a request without it.",
83    }
84}