1use 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
20pub struct DevhostConfig {
22 pub url: String,
23}
24
25#[derive(Deserialize, Serialize)]
26struct DevhostRequestInfo {
28 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 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}