Skip to main content

braid_http_session/
lib.rs

1// Copyright (C) The Strand-Braid Authors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use std::sync::{Arc, RwLock};
5use strand_bui_backend_session::HttpSession;
6use tracing::{debug, error};
7
8#[derive(thiserror::Error, Debug)]
9pub enum Error {
10    #[error("{0}")]
11    FlydraTypesError(#[from] braid_types::FlydraTypesError),
12    #[error("{0}")]
13    JsonError(#[from] serde_json::Error),
14    #[error("{0}")]
15    HyperError(#[from] hyper::Error),
16    #[error("{0}")]
17    BuiBackendSession(#[from] strand_bui_backend_session::Error),
18    #[error("HTTP error {0} when calling {1}")]
19    HttpError(hyper::StatusCode, String),
20}
21
22/// Create a `MainbrainSession` which has already made a request
23#[tracing::instrument(level = "info")]
24pub async fn create_mainbrain_session(
25    dest: strand_bui_backend_session_types::BuiServerAddrInfo,
26    jar: Arc<RwLock<cookie_store::CookieStore>>,
27) -> Result<MainbrainSession, strand_bui_backend_session::Error> {
28    debug!("requesting session with mainbrain at {:?}", dest);
29    let inner = strand_bui_backend_session::create_session(&dest, jar).await?;
30    Ok(MainbrainSession { inner })
31}
32
33fn body_from_buf(body_buf: &[u8]) -> axum::body::Body {
34    axum::body::Body::new(http_body_util::Full::new(bytes::Bytes::from(
35        body_buf.to_vec(),
36    )))
37}
38
39/// This allows communicating with the Mainbrain over HTTP RPC.
40///
41/// This replaced the old ROS layer for camera -> mainbrain command and control
42/// communication from flydra.
43#[derive(Clone, Debug)]
44pub struct MainbrainSession {
45    inner: HttpSession,
46}
47
48impl MainbrainSession {
49    #[tracing::instrument(skip_all)]
50    async fn do_post(&mut self, bytes: Vec<u8>) -> Result<(), Error> {
51        let body = body_from_buf(&bytes);
52
53        debug!("calling mainbrain callback handler");
54        let _resp = self.inner.post("callback", body).await?;
55        Ok(())
56    }
57
58    pub async fn get_remote_info(
59        &mut self,
60        raw_cam_name: &braid_types::RawCamName,
61    ) -> Result<braid_types::RemoteCameraInfoResponse, Error> {
62        let path = format!(
63            "{}/{}",
64            braid_types::braid_http::REMOTE_CAMERA_INFO_PATH,
65            braid_types::braid_http::encode_cam_name(raw_cam_name)
66        );
67
68        debug!(
69            "Getting remote camera info for camera \"{}\".",
70            raw_cam_name.as_str()
71        );
72
73        let resp = self.inner.get(&path).await?;
74
75        if !resp.status().is_success() {
76            error!("error: GET was not a success {}:{}", file!(), line!());
77            return Err(Error::HttpError(resp.status(), path));
78        };
79
80        // fold all chunks into one Vec<u8>
81        let body = resp.into_body();
82        let chunks: Result<http_body_util::Collected<bytes::Bytes>, hyper::Error> = {
83            use http_body_util::BodyExt;
84            body.collect().await
85        };
86        let data = chunks?.to_bytes();
87
88        // parse data
89        Ok(serde_json::from_slice::<
90            braid_types::RemoteCameraInfoResponse,
91        >(&data)?)
92    }
93
94    #[tracing::instrument(skip_all)]
95    pub async fn post_callback_message(
96        &mut self,
97        msg: braid_types::BraidHttpApiCallback,
98    ) -> Result<(), Error> {
99        let bytes = serde_json::to_vec(&msg).unwrap();
100        self.do_post(bytes).await
101    }
102}