Skip to main content

ota_lib/
setup.rs

1// Copyright 2019 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::{Context as _, Error};
6use fuchsia_async as fasync;
7use hyper::{Body, Request, Response};
8use serde::{Deserialize, Serialize};
9use std::convert::Infallible;
10use std::future::Future;
11use std::net::{IpAddr, SocketAddr};
12
13const SERVER_PORT: u16 = 8880;
14
15pub enum SetupEvent {
16    Root,
17    DevhostOta { cfg: DevhostConfig },
18}
19
20/// Devhost configuration, passed to the actual OTA process.
21pub struct DevhostConfig {
22    pub url: String,
23}
24
25#[derive(Deserialize, Serialize)]
26/// Configuration provided by the host for the devhost OTA. Only used for de/serialization.
27struct DevhostRequestInfo {
28    /// We assume that the OTA server is running on the requester's address
29    /// at the given port.
30    pub port: u16,
31}
32
33async fn parse_ota_json(
34    request: Request<Body>,
35    remote_addr: IpAddr,
36) -> Result<DevhostConfig, Error> {
37    use bytes::Buf as _;
38
39    let body = hyper::body::aggregate(request.into_body()).await.context("read request")?;
40    let DevhostRequestInfo { port } =
41        serde_json::from_reader(body.reader()).context("Failed to parse JSON")?;
42
43    let url = format!("http://{}/config.json", SocketAddr::new(remote_addr, port));
44    Ok(DevhostConfig { url })
45}
46
47async fn serve<Fut, F>(
48    request: Request<Body>,
49    remote_addr: SocketAddr,
50    handler: F,
51) -> Response<Body>
52where
53    Fut: Future<Output = ()>,
54    F: FnOnce(SetupEvent) -> Fut,
55{
56    use hyper::{Method, StatusCode};
57
58    match (request.method(), request.uri().path()) {
59        (&Method::GET, "/") => {
60            let () = handler(SetupEvent::Root).await;
61            Response::new("Root document".into())
62        }
63        (&Method::POST, "/ota/devhost") => {
64            // get devhost info out of POST request.
65            match parse_ota_json(request, remote_addr.ip()).await {
66                Err(e) => {
67                    let mut response = Response::new(format!("Bad request: {:?}", e).into());
68                    *response.status_mut() = StatusCode::BAD_REQUEST;
69                    response
70                }
71                Ok(cfg) => {
72                    let () = handler(SetupEvent::DevhostOta { cfg }).await;
73                    Response::new("Started OTA".into())
74                }
75            }
76        }
77        _ => {
78            let mut response = Response::new("Unknown command".into());
79            *response.status_mut() = StatusCode::NOT_FOUND;
80            response
81        }
82    }
83}
84
85pub fn start_server<Fut, F>(handler: F) -> impl Future<Output = Result<(), hyper::Error>>
86where
87    Fut: Future<Output = ()>,
88    F: FnOnce(SetupEvent) -> Fut,
89    Fut: Send + 'static,
90    F: Clone + Send + 'static,
91{
92    use futures::{FutureExt as _, TryStreamExt as _};
93    use hyper::service::{make_service_fn, service_fn};
94
95    println!("recovery: start_server");
96
97    let addr = SocketAddr::new(IpAddr::V6(std::net::Ipv6Addr::UNSPECIFIED), SERVER_PORT);
98    let listener = fasync::net::TcpListener::bind(&addr).expect("bind");
99    let listener = listener
100        .accept_stream()
101        .map_ok(|(stream, _): (_, SocketAddr)| fuchsia_hyper::TcpStream { stream });
102
103    let make_svc = make_service_fn(move |fuchsia_hyper::TcpStream { stream }| {
104        let handler = handler.clone();
105        std::future::ready((|| {
106            let remote_addr = stream.std().peer_addr().context("peer addr")?;
107            Ok::<_, Error>(service_fn(move |request| {
108                let handler = handler.clone();
109                serve(request, remote_addr, handler).map(Ok::<_, Infallible>)
110            }))
111        })())
112    });
113
114    hyper::Server::builder(hyper::server::accept::from_stream(listener))
115        .executor(fuchsia_hyper::Executor)
116        .serve(make_svc)
117}