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
37impl EspHttpClient {
38 pub fn new(stack: Stack<'static>, tls_seed: u64) -> Self {
40 Self { stack, tls_seed }
41 }
42
43 pub async fn request(
49 &self,
50 method: reqwless::request::Method,
51 url: &str,
52 body: Option<&[u8]>,
53 ) -> Result<http::Response, Error> {
54 const MAX_REDIRECTS: u8 = 5;
55 let mut current_url = alloc::string::String::from(url);
56
57 for _ in 0..=MAX_REDIRECTS {
58 let url_this_iter = current_url.clone();
59
60 let dns = DnsSocket::new(self.stack);
61 let tcp_state = alloc::boxed::Box::new(TcpClientState::<1, 4096, 4096>::new());
62 let tcp = TcpClient::new(self.stack, &*tcp_state);
63
64 let mut rx_buffer = alloc::vec![0u8; 16640].into_boxed_slice();
65 let mut tx_buffer = alloc::vec![0u8; 16640].into_boxed_slice();
66 let mut response_buffer = alloc::vec![0u8; 524288].into_boxed_slice();
67
68 let tls = TlsConfig::new(
69 self.tls_seed,
70 &mut rx_buffer,
71 &mut tx_buffer,
72 reqwless::client::TlsVerify::None,
73 );
74
75 let mut client = HttpClient::new_with_tls(&tcp, &dns, tls);
76 info!("HTTP {} {}", method as u8, url_this_iter.as_str());
77 let mut request = client
78 .request(method, url_this_iter.as_str())
79 .await?
80 .body(body);
81
82 let response = request.send(&mut response_buffer).await?;
83 let status = response.status.0;
84 info!("Response status: {}", status);
85
86 if !(300u16..400).contains(&status) {
88 if !(200u16..300).contains(&status) {
89 warn!("Request failed with status {}", status);
90 }
91 let body_bytes = response.body().read_to_end().await?;
92 info!("Response complete: {} bytes", body_bytes.len());
93 return Ok(http::Response {
94 status,
95 bytes: body_bytes.to_vec(),
96 content_length: Some(u64::try_from(body_bytes.len()).unwrap_or(0)),
97 });
98 }
99
100 let location = response
102 .headers()
103 .find(|(name, _)| name.eq_ignore_ascii_case("location"))
104 .and_then(|(_, value)| core::str::from_utf8(value).ok())
105 .map(alloc::string::String::from);
106
107 match location {
108 Some(new_url) => {
109 info!("Following redirect {} -> {}", status, new_url.as_str());
110 current_url = new_url;
111 }
112 None => {
113 warn!("Redirect response missing Location header");
114 return Err(Error::Codec);
115 }
116 }
117 }
118
119 warn!("Max redirects ({}) reached", MAX_REDIRECTS);
120 Err(Error::Codec)
121 }
122}