File
Blob: archive/fft-benchmark/firmware/crates/esp32-radio/src/platform/http.rs
| 1 | //! Blocking certificate-verified HTTPS, owned exclusively by signaling. |
| 2 | use super::ffi; |
| 3 | use crate::error::{Error, Result}; |
| 4 | use std::{ |
| 5 | ffi::{CStr, c_void}, |
| 6 | marker::PhantomData, |
| 7 | ptr::NonNull, |
| 8 | rc::Rc, |
| 9 | sync::atomic::{AtomicBool, Ordering}, |
| 10 | }; |
| 11 | |
| 12 | static HTTP_TAKEN: AtomicBool = AtomicBool::new(false); |
| 13 | pub(crate) const RESPONSE_LIMIT: usize = 24_576; |
| 14 | |
| 15 | #[derive(Debug)] |
| 16 | pub(crate) struct Http { |
| 17 | handle: NonNull<c_void>, |
| 18 | _task: PhantomData<Rc<()>>, |
| 19 | } |
| 20 | |
| 21 | impl Http { |
| 22 | pub(crate) fn open() -> Result<Self> { |
| 23 | if HTTP_TAKEN.swap(true, Ordering::AcqRel) { |
| 24 | return Err(Error::new("HTTP task already initialized")); |
| 25 | } |
| 26 | loop { |
| 27 | // SAFETY: only the successful guard owner may initialize/synchronize |
| 28 | // SNTP. C owns all configuration; no Rust callback or borrowed memory. |
| 29 | match unsafe { ffi::radio_clock_sync() } { |
| 30 | 1 => break, |
| 31 | -1 => return Err(Error::new("clock initialization failed")), |
| 32 | _ => super::log("Waiting for time synchronization before verified HTTPS"), |
| 33 | } |
| 34 | } |
| 35 | // SAFETY: C returns a fresh client owning all callback buffers. It uses |
| 36 | // compiled credentials internally; no secret enters Rust diagnostics. |
| 37 | let handle = NonNull::new(unsafe { ffi::radio_http_open() }) |
| 38 | .ok_or(Error::new("HTTPS initialization failed"))?; |
| 39 | Ok(Self { |
| 40 | handle, |
| 41 | _task: PhantomData, |
| 42 | }) |
| 43 | } |
| 44 | pub(crate) fn post( |
| 45 | &mut self, |
| 46 | path: &CStr, |
| 47 | body: &[u8], |
| 48 | out: &mut [u8], |
| 49 | ) -> Result<(i32, usize)> { |
| 50 | let mut used = 0; |
| 51 | // SAFETY: all pointers are live for this blocking call; output is uniquely |
| 52 | // borrowed and disjoint from body. C clears its borrowed POST field before |
| 53 | // return. Events use C-owned response storage, then copy within capacity. |
| 54 | let status = unsafe { |
| 55 | ffi::radio_http_post( |
| 56 | self.handle.as_ptr(), |
| 57 | path.as_ptr(), |
| 58 | body.as_ptr(), |
| 59 | body.len(), |
| 60 | out.as_mut_ptr(), |
| 61 | out.len(), |
| 62 | &mut used, |
| 63 | ) |
| 64 | }; |
| 65 | if used > out.len() { |
| 66 | return Err(Error::new("invalid HTTPS response length")); |
| 67 | } |
| 68 | Ok((status, used)) |
| 69 | } |
| 70 | } |
| 71 | impl Drop for Http { |
| 72 | fn drop(&mut self) { |
| 73 | // SAFETY: all blocking calls/callbacks have returned; same task and sole |
| 74 | // owner. C cleans up the client before freeing its callback context. |
| 75 | unsafe { ffi::radio_http_free(self.handle.as_ptr()) }; |
| 76 | } |
| 77 | } |