embedded_wg_display/http_client/
mod.rs1use crate::runtime::widget::widget::http;
20use defmt::{info, warn};
21use embassy_net::{
22 Stack,
23 dns::DnsSocket,
24 tcp::client::{TcpClient, TcpClientState},
25};
26use reqwless::{
27 Error,
28 client::{HttpClient, TlsConfig},
29 request::RequestBuilder,
30};
31
32pub struct EspHttpClient {
33 stack: Stack<'static>,
34 tls_seed: u64,
35}
36
37const MAX_RESPONSE_SIZE: usize = 1024 * 1024; const MAX_REDIRECTS: u8 = 5;
39
40impl EspHttpClient {
41 pub fn new(stack: Stack<'static>, tls_seed: u64) -> Self {
43 Self { stack, tls_seed }
44 }
45
46 pub async fn request(
53 &self,
54 method: reqwless::request::Method,
55 url: &str,
56 body: Option<&[u8]>,
57 ) -> Result<http::Response, Error> {
58 let mut current_url = alloc::string::String::from(url);
59
60 for _ in 0..=MAX_REDIRECTS {
61 let url_this_iter = current_url.clone();
62
63 let dns = DnsSocket::new(self.stack);
64 let tcp_state = alloc::boxed::Box::new(TcpClientState::<1, 4096, 4096>::new());
65 let tcp = TcpClient::new(self.stack, &*tcp_state);
66
67 let mut rx_buffer = alloc::vec![0u8; 16640].into_boxed_slice();
68 let mut tx_buffer = alloc::vec![0u8; 16640].into_boxed_slice();
69 let mut response_buffer = alloc::vec![0u8; MAX_RESPONSE_SIZE].into_boxed_slice();
70
71 let tls = TlsConfig::new(
72 self.tls_seed,
73 &mut rx_buffer,
74 &mut tx_buffer,
75 reqwless::client::TlsVerify::None,
76 );
77
78 let mut client = HttpClient::new_with_tls(&tcp, &dns, tls);
79 info!("HTTP {} {}", method as u8, url_this_iter.as_str());
80 let mut request = client
81 .request(method, url_this_iter.as_str())
82 .await?
83 .body(body);
84
85 let response = request.send(&mut response_buffer).await?;
86 let status = response.status.0;
87 info!("Response status: {}", status);
88
89 if !(300u16..400).contains(&status) {
91 if !(200u16..300).contains(&status) {
92 warn!("Request failed with status {}", status);
93 }
94 let body_bytes = response.body().read_to_end().await?;
95 info!("Response complete: {} bytes", body_bytes.len());
96 return Ok(http::Response {
97 status,
98 bytes: body_bytes.to_vec(),
99 content_length: Some(u64::try_from(body_bytes.len()).unwrap_or(0)),
100 });
101 }
102
103 let location = response
105 .headers()
106 .find(|(name, _)| name.eq_ignore_ascii_case("location"))
107 .and_then(|(_, value)| core::str::from_utf8(value).ok())
108 .map(alloc::string::String::from);
109
110 match location {
111 Some(new_url) => {
112 info!("Following redirect {} -> {}", status, new_url.as_str());
113 current_url = new_url;
114 }
115 None => {
116 warn!("Redirect response missing Location header");
117 return Err(Error::Codec);
118 }
119 }
120 }
121
122 warn!("Max redirects ({}) reached", MAX_REDIRECTS);
123 Err(Error::Codec)
124 }
125}