1use std::sync::Arc;
17
18use axum::{extract::State, response::IntoResponse, routing::post};
19use endhost_api_models::{PathDiscovery, UnderlayDiscovery};
20use endhost_api_protobuf::endhost::api_service::v1::{
21 ListSegmentsRequest, ListSegmentsResponse, ListUnderlaysRequest, ListUnderlaysResponse,
22};
23use scion_proto::{address::IsdAsn, path::SegmentsError};
24use scion_sdk_axum_connect_rpc::extractor::ConnectRpc;
25
26pub const ENDHOST_API_V1: &str = "scion.endhost.v1";
28
29pub const UNDERLAY_SERVICE: &str = "UnderlayService";
31pub const PATH_SERVICE: &str = "PathService";
33
34pub const LIST_UNDERLAYS: &str = "/ListUnderlays";
36pub const LIST_PATHS: &str = "/ListPaths";
38
39pub fn nest_endhost_api(
41 base_router: axum::Router,
42 underlay_service: Arc<dyn UnderlayDiscovery>,
43 path_service: Arc<dyn PathDiscovery>,
44) -> axum::Router {
45 let underlay_router = axum::Router::new()
46 .route(LIST_UNDERLAYS, post(list_underlays_handler))
47 .with_state(underlay_service);
48 let base_router = base_router.nest(
49 &service_path(ENDHOST_API_V1, UNDERLAY_SERVICE),
50 underlay_router,
51 );
52
53 let path_router = axum::Router::new()
54 .route(LIST_PATHS, post(list_segments_handler))
55 .with_state(path_service);
56 base_router.nest(&service_path(ENDHOST_API_V1, PATH_SERVICE), path_router)
57}
58
59async fn list_underlays_handler(
60 State(underlay_service): State<Arc<dyn UnderlayDiscovery>>,
61 ConnectRpc(request): ConnectRpc<ListUnderlaysRequest>,
62) -> ConnectRpc<ListUnderlaysResponse> {
63 ConnectRpc(
64 underlay_service
65 .list_underlays(request.isd_as.map(IsdAsn::from).unwrap_or(IsdAsn::WILDCARD))
66 .into(),
67 )
68}
69
70async fn list_segments_handler(
71 State(path_service): State<Arc<dyn PathDiscovery>>,
72 ConnectRpc(request): ConnectRpc<ListSegmentsRequest>,
73) -> Result<ConnectRpc<ListSegmentsResponse>, axum::response::Response> {
74 let (src, dst) = (
75 IsdAsn::from(request.src_isd_as),
76 IsdAsn::from(request.dst_isd_as),
77 );
78 match path_service
79 .list_segments(src, dst, request.page_size, request.page_token)
80 .await
81 {
82 Ok(segments) => Ok(ConnectRpc(segments.into())),
83 Err(SegmentsError::InvalidArgument(msg)) => {
84 Err((axum::http::StatusCode::BAD_REQUEST, msg).into_response())
85 }
86 Err(SegmentsError::InternalError(msg)) => {
87 Err((axum::http::StatusCode::INTERNAL_SERVER_ERROR, msg).into_response())
88 }
89 }
90}
91
92fn service_path(api: &str, service: &str) -> String {
93 format!("/{api}.{service}")
94}