702 lines
22 KiB
Rust
702 lines
22 KiB
Rust
//offer ota and config mode
|
|
|
|
use std::{
|
|
str::from_utf8,
|
|
sync::{atomic::AtomicBool, Arc},
|
|
};
|
|
use crate::{
|
|
espota::OtaUpdate, get_version, log::LogMessage, map_range_moisture, plant_hal::{FileInfo, PLANT_COUNT}, BOARD_ACCESS
|
|
};
|
|
use anyhow::bail;
|
|
use chrono::DateTime;
|
|
use esp_idf_sys::{esp_set_time_from_rtc, settimeofday, timeval, vTaskDelay};
|
|
use core::result::Result::Ok;
|
|
use embedded_svc::http::Method;
|
|
use esp_idf_hal::delay::Delay;
|
|
use esp_idf_svc::http::server::{Configuration, EspHttpConnection, EspHttpServer, Request};
|
|
use heapless::String;
|
|
use serde::{Deserialize, Serialize};
|
|
use url::Url;
|
|
|
|
use crate::config::PlantControllerConfig;
|
|
|
|
#[derive(Serialize, Debug)]
|
|
struct SSIDList<'a> {
|
|
ssids: Vec<&'a String<32>>,
|
|
}
|
|
|
|
#[derive(Serialize, Debug)]
|
|
struct LoadData<'a> {
|
|
rtc: &'a str,
|
|
native: &'a str,
|
|
}
|
|
|
|
#[derive(Serialize, Debug)]
|
|
struct Moistures {
|
|
moisture_a: Vec<u8>,
|
|
moisture_b: Vec<u8>,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
struct SetTime<'a> {
|
|
time: &'a str,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
pub struct TestPump {
|
|
pump: usize,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
|
pub struct WebBackupHeader{
|
|
timestamp: std::string::String,
|
|
size: usize
|
|
}
|
|
|
|
fn write_time(
|
|
request: &mut Request<&mut EspHttpConnection>,
|
|
) -> Result<Option<std::string::String>, anyhow::Error> {
|
|
let actual_data = read_up_to_bytes_from_request(request, None)?;
|
|
let time: SetTime = serde_json::from_slice(&actual_data)?;
|
|
let parsed = DateTime::parse_from_rfc3339(time.time).map_err(|err| anyhow::anyhow!(err))?;
|
|
let mut board = BOARD_ACCESS.lock().unwrap();
|
|
|
|
let now = timeval {
|
|
tv_sec: parsed.to_utc().timestamp(),
|
|
tv_usec: 0
|
|
};
|
|
unsafe { settimeofday(&now, core::ptr::null_mut()) };
|
|
board.set_rtc_time(&parsed.to_utc())?;
|
|
anyhow::Ok(None)
|
|
}
|
|
|
|
fn get_live_moisture(
|
|
_request: &mut Request<&mut EspHttpConnection>,
|
|
) -> Result<Option<std::string::String>, anyhow::Error> {
|
|
let mut board = BOARD_ACCESS.lock().unwrap();
|
|
|
|
let mut a: Vec<u8> = Vec::new();
|
|
let mut b: Vec<u8> = Vec::new();
|
|
for plant in 0..8 {
|
|
let a_hz = board.measure_moisture_hz(plant, crate::plant_hal::Sensor::A)?;
|
|
let b_hz = board.measure_moisture_hz(plant, crate::plant_hal::Sensor::B)?;
|
|
let a_pct = map_range_moisture(a_hz as f32);
|
|
|
|
match a_pct {
|
|
Ok(result) => {
|
|
a.push(result);
|
|
}
|
|
Err(_) => {
|
|
a.push(200);
|
|
}
|
|
}
|
|
|
|
let b_pct = map_range_moisture(b_hz as f32);
|
|
match b_pct {
|
|
Ok(result) => {
|
|
b.push(result);
|
|
}
|
|
Err(_) => {
|
|
b.push(200);
|
|
}
|
|
}
|
|
}
|
|
|
|
let data = Moistures {
|
|
moisture_a: a,
|
|
moisture_b: b,
|
|
};
|
|
let json = serde_json::to_string(&data)?;
|
|
|
|
anyhow::Ok(Some(json))
|
|
}
|
|
|
|
fn get_data(
|
|
_request: &mut Request<&mut EspHttpConnection>,
|
|
) -> Result<Option<std::string::String>, anyhow::Error> {
|
|
let mut board = BOARD_ACCESS.lock().unwrap();
|
|
let native = board
|
|
.time()
|
|
.and_then(|t| Ok(t.to_rfc3339()))
|
|
.unwrap_or("error".to_string());
|
|
let rtc = board
|
|
.get_rtc_time()
|
|
.and_then(|t| Ok(t.to_rfc3339()))
|
|
.unwrap_or("error".to_string());
|
|
|
|
let data = LoadData {
|
|
rtc: rtc.as_str(),
|
|
native: native.as_str(),
|
|
};
|
|
let json = serde_json::to_string(&data)?;
|
|
|
|
anyhow::Ok(Some(json))
|
|
}
|
|
|
|
fn get_config(
|
|
_request: &mut Request<&mut EspHttpConnection>,
|
|
) -> Result<Option<std::string::String>, anyhow::Error> {
|
|
let mut board = BOARD_ACCESS.lock().unwrap();
|
|
let json = match board.get_config() {
|
|
Ok(config) => serde_json::to_string(&config)?,
|
|
Err(_) => serde_json::to_string(&PlantControllerConfig::default())?,
|
|
};
|
|
anyhow::Ok(Some(json))
|
|
}
|
|
|
|
fn backup_config(
|
|
request: &mut Request<&mut EspHttpConnection>,
|
|
) -> Result<Option<std::string::String>, anyhow::Error> {
|
|
let all = read_up_to_bytes_from_request(request, Some(3072))?;
|
|
let mut board = BOARD_ACCESS.lock().unwrap();
|
|
board.backup_config(&all)?;
|
|
anyhow::Ok(Some("saved".to_owned()))
|
|
}
|
|
|
|
fn get_backup_config(
|
|
_request: &mut Request<&mut EspHttpConnection>,
|
|
) -> Result<Option<std::string::String>, anyhow::Error> {
|
|
let mut board = BOARD_ACCESS.lock().unwrap();
|
|
let json = match board.get_backup_config() {
|
|
Ok(config) => std::str::from_utf8(&config)?.to_owned(),
|
|
Err(err) => {
|
|
println!("Error get backup config {:?}", err);
|
|
err.to_string()
|
|
}
|
|
};
|
|
anyhow::Ok(Some(json))
|
|
}
|
|
|
|
|
|
fn backup_info(
|
|
_request: &mut Request<&mut EspHttpConnection>,
|
|
) -> Result<Option<std::string::String>, anyhow::Error> {
|
|
let mut board = BOARD_ACCESS.lock().unwrap();
|
|
let header = board.get_backup_info();
|
|
let json = match header {
|
|
Ok(h) => {
|
|
let timestamp = DateTime::from_timestamp_millis(h.timestamp).unwrap();
|
|
let wbh = WebBackupHeader{
|
|
timestamp: timestamp.to_rfc3339(),
|
|
size: h.size,
|
|
};
|
|
serde_json::to_string(&wbh)?
|
|
},
|
|
Err(_) => "{\"error\":\"Header could not be parsed\"".to_owned()
|
|
};
|
|
anyhow::Ok(Some(json))
|
|
}
|
|
|
|
fn set_config(
|
|
request: &mut Request<&mut EspHttpConnection>,
|
|
) -> Result<Option<std::string::String>, anyhow::Error> {
|
|
let all = read_up_to_bytes_from_request(request, Some(3072))?;
|
|
let config: PlantControllerConfig = serde_json::from_slice(&all)?;
|
|
let mut board = BOARD_ACCESS.lock().unwrap();
|
|
board.set_config(&config)?;
|
|
anyhow::Ok(Some("saved".to_owned()))
|
|
}
|
|
|
|
|
|
fn get_battery_state(
|
|
_request: &mut Request<&mut EspHttpConnection>,
|
|
) -> Result<Option<std::string::String>, anyhow::Error> {
|
|
let mut board = BOARD_ACCESS.lock().unwrap();
|
|
let battery_state = board.get_battery_state();
|
|
let battery_json = serde_json::to_string(&battery_state)?;
|
|
anyhow::Ok(Some(battery_json))
|
|
}
|
|
|
|
fn get_log(
|
|
_request: &mut Request<&mut EspHttpConnection>,
|
|
) -> Result<Option<std::string::String>, anyhow::Error> {
|
|
let output = crate::log::get_log();
|
|
anyhow::Ok(Some(serde_json::to_string(&output)?))
|
|
}
|
|
|
|
fn get_log_localization_config() -> Result<std::string::String, anyhow::Error> {
|
|
anyhow::Ok(serde_json::to_string(&LogMessage::to_log_localisation_config())?)
|
|
}
|
|
|
|
fn get_version_web(
|
|
_request: &mut Request<&mut EspHttpConnection>,
|
|
) -> Result<Option<std::string::String>, anyhow::Error> {
|
|
|
|
anyhow::Ok(Some(serde_json::to_string(&get_version())?))
|
|
}
|
|
|
|
fn pump_test(
|
|
request: &mut Request<&mut EspHttpConnection>,
|
|
) -> Result<Option<std::string::String>, anyhow::Error> {
|
|
let actual_data = read_up_to_bytes_from_request(request, None)?;
|
|
let pump_test: TestPump = serde_json::from_slice(&actual_data)?;
|
|
let mut board = BOARD_ACCESS.lock().unwrap();
|
|
board.test_pump(pump_test.pump)?;
|
|
anyhow::Ok(None)
|
|
}
|
|
|
|
fn wifi_scan(
|
|
_request: &mut Request<&mut EspHttpConnection>,
|
|
) -> Result<Option<std::string::String>, anyhow::Error> {
|
|
let mut board = BOARD_ACCESS.lock().unwrap();
|
|
let scan_result = board.wifi_scan()?;
|
|
let mut ssids: Vec<&String<32>> = Vec::new();
|
|
scan_result.iter().for_each(|s| ssids.push(&s.ssid));
|
|
let ssid_json = serde_json::to_string(&SSIDList { ssids })?;
|
|
println!("Sending ssid list {}", &ssid_json);
|
|
anyhow::Ok(Some(ssid_json))
|
|
}
|
|
|
|
fn list_files(
|
|
request: &mut Request<&mut EspHttpConnection>,
|
|
) -> Result<Option<std::string::String>, anyhow::Error> {
|
|
let filename = query_param(request.uri(), "filename").unwrap_or_default();
|
|
let board = BOARD_ACCESS.lock().unwrap();
|
|
let result = board.list_files(&filename);
|
|
let file_list_json = serde_json::to_string(&result)?;
|
|
return anyhow::Ok(Some(file_list_json));
|
|
}
|
|
|
|
fn ota(
|
|
request: &mut Request<&mut EspHttpConnection>,
|
|
) -> Result<Option<std::string::String>, anyhow::Error> {
|
|
let mut board = BOARD_ACCESS.lock().unwrap();
|
|
let mut ota = OtaUpdate::begin()?;
|
|
println!("start ota");
|
|
|
|
//having a larger buffer is not really faster, requires more stack and prevents the progress bar from working ;)
|
|
const BUFFER_SIZE: usize = 512;
|
|
let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
|
|
let mut total_read: usize = 0;
|
|
let mut lastiter = 0;
|
|
loop {
|
|
let read = request.read(&mut buffer)?;
|
|
total_read += read;
|
|
println!("received {read} bytes ota {total_read}");
|
|
let to_write = &buffer[0..read];
|
|
|
|
let iter = (total_read/1024)%8;
|
|
if iter != lastiter {
|
|
for i in 0..PLANT_COUNT {
|
|
board.fault(i, iter==i);
|
|
|
|
}
|
|
lastiter = iter;
|
|
}
|
|
|
|
ota.write(to_write)?;
|
|
println!("wrote {read} bytes ota {total_read}");
|
|
if read == 0 {
|
|
break;
|
|
}
|
|
}
|
|
println!("finish ota");
|
|
let partition = ota.raw_partition();
|
|
println!("finalizing and changing boot partition to {partition:?}");
|
|
|
|
let mut finalizer = ota.finalize()?;
|
|
println!("changing boot partition");
|
|
board.set_restart_to_conf(true);
|
|
drop(board);
|
|
finalizer.set_as_boot_partition()?;
|
|
finalizer.restart();
|
|
}
|
|
|
|
fn flash_bq(filename: &str, dryrun: bool) -> anyhow::Result<()> {
|
|
let mut board = BOARD_ACCESS.lock().unwrap();
|
|
|
|
let mut toggle = true;
|
|
let delay = Delay::new(1);
|
|
|
|
let file_handle = board.get_file_handle(filename, false)?;
|
|
|
|
let mut reader = std::io::BufRead::lines(std::io::BufReader::with_capacity(512, file_handle));
|
|
let mut line = 0;
|
|
loop {
|
|
board.general_fault(toggle);
|
|
toggle = !toggle;
|
|
|
|
delay.delay_us(2);
|
|
line += 1;
|
|
match reader.next() {
|
|
Some(next) => {
|
|
let input = next?;
|
|
println!("flashing bq34z100 dryrun:{dryrun} line {line} payload: {input}");
|
|
match board.flash_bq34_z100(&input, dryrun) {
|
|
Ok(_) => {
|
|
println!("ok")
|
|
}
|
|
Err(err) => {
|
|
bail!(
|
|
"Error flashing bq34z100 in dryrun: {dryrun} line: {line} error: {err}"
|
|
)
|
|
}
|
|
}
|
|
}
|
|
None => break,
|
|
}
|
|
}
|
|
println!("Finished flashing file {line} lines processed");
|
|
board.general_fault(false);
|
|
return anyhow::Ok(());
|
|
}
|
|
|
|
fn query_param(uri: &str, param_name: &str) -> Option<std::string::String> {
|
|
println!("{uri} get {param_name}");
|
|
let parsed = Url::parse(&format!("http://127.0.0.1/{uri}")).unwrap();
|
|
let value = parsed.query_pairs().filter(|it| it.0 == param_name).next();
|
|
match value {
|
|
Some(found) => {
|
|
return Some(found.1.into_owned());
|
|
}
|
|
None => return None,
|
|
}
|
|
}
|
|
|
|
pub fn httpd(reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
|
|
let server_config = Configuration {
|
|
stack_size: 32768,
|
|
..Default::default()
|
|
};
|
|
let mut server: Box<EspHttpServer<'static>> =
|
|
Box::new(EspHttpServer::new(&server_config).unwrap());
|
|
server
|
|
.fn_handler("/version", Method::Get, |request| {
|
|
handle_error_to500(request, get_version_web)
|
|
})
|
|
.unwrap();
|
|
server
|
|
.fn_handler("/log", Method::Get, |request| {
|
|
handle_error_to500(request, get_log)
|
|
})
|
|
.unwrap();
|
|
server.fn_handler("/log_localization", Method::Get, |request| {
|
|
cors_response(request, 200, &get_log_localization_config().unwrap())
|
|
|
|
})
|
|
.unwrap();
|
|
server
|
|
.fn_handler("/battery", Method::Get, |request| {
|
|
handle_error_to500(request, get_battery_state)
|
|
})
|
|
.unwrap();
|
|
server
|
|
.fn_handler("/time", Method::Get, |request| {
|
|
handle_error_to500(request, get_data)
|
|
})
|
|
.unwrap();
|
|
server
|
|
.fn_handler("/moisture", Method::Get, |request| {
|
|
handle_error_to500(request, get_live_moisture)
|
|
})
|
|
.unwrap();
|
|
server
|
|
.fn_handler("/time", Method::Post, |request| {
|
|
handle_error_to500(request, write_time)
|
|
})
|
|
.unwrap();
|
|
server
|
|
.fn_handler("/pumptest", Method::Post, |request| {
|
|
handle_error_to500(request, pump_test)
|
|
})
|
|
.unwrap();
|
|
server
|
|
.fn_handler("/boardtest", Method::Post, move |_| {
|
|
BOARD_ACCESS.lock().unwrap().test()
|
|
})
|
|
.unwrap();
|
|
server
|
|
.fn_handler("/wifiscan", Method::Post, move |request| {
|
|
handle_error_to500(request, wifi_scan)
|
|
})
|
|
.unwrap();
|
|
server
|
|
.fn_handler("/ota", Method::Post, |request| {
|
|
handle_error_to500(request, ota)
|
|
})
|
|
.unwrap();
|
|
server
|
|
.fn_handler("/ota", Method::Options, |request| {
|
|
cors_response(request, 200, "")
|
|
})
|
|
.unwrap();
|
|
server
|
|
.fn_handler("/get_config", Method::Get, move |request| {
|
|
handle_error_to500(request, get_config)
|
|
})
|
|
.unwrap();
|
|
server
|
|
.fn_handler("/get_backup_config", Method::Get, move |request| {
|
|
handle_error_to500(request, get_backup_config)
|
|
})
|
|
.unwrap();
|
|
|
|
server
|
|
.fn_handler("/set_config", Method::Post, move |request| {
|
|
handle_error_to500(request, set_config)
|
|
})
|
|
.unwrap();
|
|
server
|
|
.fn_handler("/backup_config", Method::Post, move |request| {
|
|
handle_error_to500(request, backup_config)
|
|
})
|
|
.unwrap();
|
|
server
|
|
.fn_handler("/backup_info", Method::Get, move |request| {
|
|
handle_error_to500(request, backup_info)
|
|
})
|
|
.unwrap();
|
|
server
|
|
.fn_handler("/files", Method::Get, move |request| {
|
|
handle_error_to500(request, list_files)
|
|
})
|
|
.unwrap();
|
|
let reboot_now_for_reboot = reboot_now.clone();
|
|
server
|
|
.fn_handler("/reboot", Method::Post, move |_| {
|
|
BOARD_ACCESS
|
|
.lock()
|
|
.unwrap()
|
|
.set_restart_to_conf(true);
|
|
reboot_now_for_reboot.store(true, std::sync::atomic::Ordering::Relaxed);
|
|
anyhow::Ok(())
|
|
})
|
|
.unwrap();
|
|
|
|
unsafe { vTaskDelay(1) };
|
|
|
|
let reboot_now_for_exit = reboot_now.clone();
|
|
server
|
|
.fn_handler("/exit", Method::Post, move |_| {
|
|
reboot_now_for_exit.store(true, std::sync::atomic::Ordering::Relaxed);
|
|
anyhow::Ok(())
|
|
})
|
|
.unwrap();
|
|
server
|
|
.fn_handler("/file", Method::Get, move |request| {
|
|
let filename = query_param(request.uri(), "filename").unwrap();
|
|
let file_handle = BOARD_ACCESS
|
|
.lock()
|
|
.unwrap()
|
|
.get_file_handle(&filename, false);
|
|
match file_handle {
|
|
Ok(mut file_handle) => {
|
|
let headers = [("Access-Control-Allow-Origin", "*")];
|
|
let mut response = request.into_response(200, None, &headers)?;
|
|
const BUFFER_SIZE: usize = 512;
|
|
let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
|
|
let mut total_read: usize = 0;
|
|
loop {
|
|
unsafe { vTaskDelay(1) };
|
|
let read = std::io::Read::read(&mut file_handle, &mut buffer)?;
|
|
total_read += read;
|
|
let to_write = &buffer[0..read];
|
|
response.write(to_write)?;
|
|
if read == 0 {
|
|
break;
|
|
}
|
|
}
|
|
println!("wrote {total_read} for file {filename}");
|
|
drop(file_handle);
|
|
response.flush()?;
|
|
}
|
|
Err(err) => {
|
|
//todo set headers here for filename to be error
|
|
let error_text = err.to_string();
|
|
println!("error handling get file {}", error_text);
|
|
cors_response(request, 500, &error_text)?;
|
|
}
|
|
}
|
|
anyhow::Ok(())
|
|
})
|
|
.unwrap();
|
|
server
|
|
.fn_handler("/file", Method::Post, move |mut request| {
|
|
let filename = query_param(request.uri(), "filename").unwrap();
|
|
let lock = BOARD_ACCESS
|
|
.lock()
|
|
.unwrap();
|
|
let file_handle =
|
|
lock.get_file_handle(&filename, true);
|
|
match file_handle {
|
|
//TODO get free filesystem size, check against during write if not to large
|
|
|
|
Ok(mut file_handle) => {
|
|
const BUFFER_SIZE: usize = 512;
|
|
let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
|
|
let mut total_read: usize = 0;
|
|
let mut lastiter = 0;
|
|
loop {
|
|
|
|
let iter = (total_read/1024)%8;
|
|
if iter != lastiter {
|
|
for i in 0..PLANT_COUNT {
|
|
lock.fault(i, iter==i);
|
|
}
|
|
lastiter = iter;
|
|
}
|
|
|
|
let read = request.read(&mut buffer)?;
|
|
total_read += read;
|
|
let to_write = &buffer[0..read];
|
|
std::io::Write::write(&mut file_handle, to_write)?;
|
|
if read == 0 {
|
|
break;
|
|
}
|
|
}
|
|
cors_response(request, 200, &format!("saved {total_read} bytes"))?;
|
|
}
|
|
Err(err) => {
|
|
//todo set headers here for filename to be error
|
|
let error_text = err.to_string();
|
|
println!("error handling get file {}", error_text);
|
|
cors_response(request, 500, &error_text)?;
|
|
}
|
|
}
|
|
drop(lock);
|
|
anyhow::Ok(())
|
|
})
|
|
.unwrap();
|
|
|
|
server
|
|
.fn_handler("/file", Method::Delete, move |request| {
|
|
let filename = query_param(request.uri(), "filename").unwrap();
|
|
let copy = filename.clone();
|
|
let board = BOARD_ACCESS.lock().unwrap();
|
|
match board.delete_file(&filename) {
|
|
Ok(_) => {
|
|
let info = format!("Deleted file {copy}");
|
|
cors_response(request, 200, &info)?;
|
|
}
|
|
Err(err) => {
|
|
let info = format!("Could not delete file {copy} {err:?}");
|
|
cors_response(request, 400, &info)?;
|
|
}
|
|
}
|
|
anyhow::Ok(())
|
|
})
|
|
.unwrap();
|
|
server
|
|
.fn_handler("/file", Method::Options, |request| {
|
|
cors_response(request, 200, "")
|
|
})
|
|
.unwrap();
|
|
|
|
server
|
|
.fn_handler("/flashbattery", Method::Post, move |request| {
|
|
let filename = query_param(request.uri(),"filename").unwrap();
|
|
let dryrun = true;
|
|
match flash_bq(&filename, false) {
|
|
Ok(_) => {
|
|
if !dryrun {
|
|
match flash_bq(&filename, true) {
|
|
Ok(_) => {
|
|
cors_response(request, 200, "Sucessfully flashed bq34z100")?;
|
|
},
|
|
Err(err) => {
|
|
let info = format!("Could not flash bq34z100, could be bricked now! {filename} {err:?}");
|
|
cors_response(request, 500, &info)?;
|
|
},
|
|
}
|
|
} else {
|
|
cors_response(request, 200, "Sucessfully processed bq34z100")?;
|
|
}
|
|
},
|
|
Err(err) => {
|
|
let info = format!("Could not process firmware file for, bq34z100, refusing to flash! {filename} {err:?}");
|
|
cors_response(request, 500, &info)?;
|
|
},
|
|
};
|
|
anyhow::Ok(())
|
|
})
|
|
.unwrap();
|
|
unsafe { vTaskDelay(1) };
|
|
server
|
|
.fn_handler("/", Method::Get, move |request| {
|
|
let mut response = request.into_ok_response()?;
|
|
response.write(include_bytes!("index.html"))?;
|
|
anyhow::Ok(())
|
|
})
|
|
.unwrap();
|
|
server
|
|
.fn_handler("/favicon.ico", Method::Get, |request| {
|
|
request
|
|
.into_ok_response()?
|
|
.write(include_bytes!("favicon.ico"))?;
|
|
anyhow::Ok(())
|
|
})
|
|
.unwrap();
|
|
server
|
|
.fn_handler("/bundle.js", Method::Get, |request| {
|
|
request
|
|
.into_ok_response()?
|
|
.write(include_bytes!("bundle.js"))?;
|
|
anyhow::Ok(())
|
|
})
|
|
.unwrap();
|
|
server
|
|
}
|
|
|
|
fn cors_response(
|
|
request: Request<&mut EspHttpConnection>,
|
|
status: u16,
|
|
body: &str,
|
|
) -> Result<(), anyhow::Error> {
|
|
let headers = [
|
|
("Access-Control-Allow-Origin", "*"),
|
|
("Access-Control-Allow-Headers", "*"),
|
|
("Access-Control-Allow-Methods", "*"),
|
|
];
|
|
let mut response = request.into_response(status, None, &headers)?;
|
|
response.write(body.as_bytes())?;
|
|
response.flush()?;
|
|
return anyhow::Ok(());
|
|
}
|
|
|
|
type AnyhowHandler =
|
|
fn(&mut Request<&mut EspHttpConnection>) -> Result<Option<std::string::String>, anyhow::Error>;
|
|
fn handle_error_to500(
|
|
mut request: Request<&mut EspHttpConnection>,
|
|
chain: AnyhowHandler,
|
|
) -> Result<(), anyhow::Error> {
|
|
let error = chain(&mut request);
|
|
match error {
|
|
Ok(answer) => match answer {
|
|
Some(json) => {
|
|
cors_response(request, 200, &json)?;
|
|
}
|
|
None => {
|
|
cors_response(request, 200, "")?;
|
|
}
|
|
},
|
|
Err(err) => {
|
|
let error_text = err.to_string();
|
|
println!("error handling process {}", error_text);
|
|
cors_response(request, 500, &error_text)?;
|
|
}
|
|
}
|
|
return anyhow::Ok(());
|
|
}
|
|
|
|
fn read_up_to_bytes_from_request(request: &mut Request<&mut EspHttpConnection<'_>>, limit: Option<usize>) -> Result<Vec<u8>, anyhow::Error> {
|
|
let max_read = limit.unwrap_or(1024);
|
|
let mut data_store = Vec::new();
|
|
let mut total_read = 0;
|
|
loop{
|
|
let mut buf = [0_u8; 64];
|
|
let read = request.read(&mut buf)?;
|
|
if read == 0 {
|
|
break;
|
|
}
|
|
let actual_data = &buf[0..read];
|
|
total_read += read;
|
|
if total_read > max_read{
|
|
bail!("Request too large {total_read} > {max_read}");
|
|
}
|
|
data_store.push(actual_data.to_owned());
|
|
}
|
|
let allvec = data_store.concat();
|
|
println!("Raw data {}", from_utf8(&allvec)?);
|
|
Ok(allvec)
|
|
}
|