1use core::cell::UnsafeCell;
36use core::fmt::Debug;
37use core::marker::PhantomData;
38#[cfg(esp_idf_lwip_ipv4)]
39use core::net::Ipv4Addr;
40#[cfg(esp_idf_lwip_ipv6)]
41use core::net::Ipv6Addr;
42use core::sync::atomic::{AtomicBool, Ordering};
43use core::time::*;
44use core::{ffi, ptr};
45
46extern crate alloc;
47use alloc::borrow::ToOwned;
48use alloc::boxed::Box;
49use alloc::collections::BTreeMap;
50use alloc::string::String;
51use alloc::string::ToString;
52use alloc::sync::Arc;
53use alloc::vec::Vec;
54
55use ::log::{info, warn};
56
57use embedded_svc::http::headers::content_type;
58use embedded_svc::io::{ErrorType, Read, Write};
59
60use esp_idf_hal::cpu::Core;
61
62use crate::sys::*;
63
64use uncased::{Uncased, UncasedStr};
65
66use crate::handle::RawHandle;
67use crate::io::EspIOError;
68use crate::private::common::Newtype;
69use crate::private::cstr::to_cstring_arg;
70use crate::private::cstr::{CStr, CString};
71use crate::private::mutex::Mutex;
72#[cfg(esp_idf_esp_https_server_enable)]
73use crate::tls::X509;
74
75pub use embedded_svc::http::server::{
76 CompositeHandler, Connection, FnHandler, Handler, Middleware, Request, Response,
77};
78pub use embedded_svc::utils::http::server::registration::*;
79
80pub use super::*;
81
82#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
83pub struct KeepAlive {
84 pub idle_secs: u32,
85 pub interval_secs: u32,
86 pub probe_count: u32,
87}
88
89impl KeepAlive {
90 pub const fn new() -> Self {
91 Self {
92 idle_secs: 5,
93 interval_secs: 5,
94 probe_count: 3,
95 }
96 }
97}
98
99impl Default for KeepAlive {
100 fn default() -> Self {
101 Self::new()
102 }
103}
104
105#[derive(Copy, Clone, Debug)]
106pub struct Configuration {
107 pub http_port: u16,
108 pub ctrl_port: u16,
109 pub https_port: u16,
110 pub core: Option<Core>,
111 pub max_sessions: usize,
112 pub session_timeout: Duration,
113 pub task_caps: u32,
114 pub stack_size: usize,
115 pub max_open_sockets: usize,
116 pub max_uri_handlers: usize,
117 pub max_resp_headers: usize,
118 pub lru_purge_enable: bool,
119 pub uri_match_wildcard: bool,
120 pub keep_alive: Option<KeepAlive>,
121 pub so_linger: Option<Duration>,
122 #[cfg(esp_idf_esp_https_server_enable)]
123 pub server_certificate: Option<X509<'static>>,
124 #[cfg(esp_idf_esp_https_server_enable)]
125 pub private_key: Option<X509<'static>>,
126}
127
128impl Default for Configuration {
129 fn default() -> Self {
130 Configuration {
131 http_port: 80,
132 ctrl_port: 32768,
133 https_port: 443,
134 core: None,
135 max_sessions: 16,
136 session_timeout: Duration::from_secs(20 * 60),
137 task_caps: (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT),
138 #[cfg(not(esp_idf_esp_https_server_enable))]
139 stack_size: 6144,
140 #[cfg(esp_idf_esp_https_server_enable)]
141 stack_size: 10240,
142 max_open_sockets: 4,
143 max_uri_handlers: 32,
144 max_resp_headers: 8,
145 lru_purge_enable: true,
146 uri_match_wildcard: false,
147 keep_alive: None,
148 so_linger: None,
149 #[cfg(esp_idf_esp_https_server_enable)]
150 server_certificate: None,
151 #[cfg(esp_idf_esp_https_server_enable)]
152 private_key: None,
153 }
154 }
155}
156
157impl From<&Configuration> for Newtype<httpd_config_t> {
158 #[allow(clippy::needless_update)]
159 fn from(conf: &Configuration) -> Self {
160 Self(httpd_config_t {
161 task_priority: 5,
162 #[cfg(any(
164 all(not(esp_idf_version_major = "4"), not(esp_idf_version_major = "5")),
165 all(
166 esp_idf_version_major = "5",
167 not(any(
168 esp_idf_version_minor = "0",
169 esp_idf_version_minor = "1",
170 esp_idf_version_minor = "2"
171 ))
172 ),
173 ))]
174 task_caps: conf.task_caps,
175 stack_size: conf.stack_size,
176 core_id: conf.core.map(|core| core.into()).unwrap_or(i32::MAX),
177 server_port: conf.http_port,
178 ctrl_port: conf.ctrl_port,
179 max_open_sockets: conf.max_open_sockets as _,
180 max_uri_handlers: conf.max_uri_handlers as _,
181 max_resp_headers: conf.max_resp_headers as _,
182 backlog_conn: 5,
183 lru_purge_enable: conf.lru_purge_enable,
184 recv_wait_timeout: 5,
185 send_wait_timeout: 5,
186 global_user_ctx: ptr::null_mut(),
187 global_user_ctx_free_fn: None,
188 global_transport_ctx: ptr::null_mut(),
189 global_transport_ctx_free_fn: None,
190 open_fn: None,
191 close_fn: None,
192 uri_match_fn: conf.uri_match_wildcard.then_some(httpd_uri_match_wildcard),
193 keep_alive_enable: conf.keep_alive.is_some(),
194 keep_alive_idle: conf.keep_alive.map(|ka| ka.idle_secs as i32).unwrap_or(0),
195 keep_alive_interval: conf
196 .keep_alive
197 .map(|ka| ka.interval_secs as i32)
198 .unwrap_or(0),
199 keep_alive_count: conf.keep_alive.map(|ka| ka.probe_count as i32).unwrap_or(0),
200 enable_so_linger: conf.so_linger.is_some(),
201 linger_timeout: conf.so_linger.map(|d| d.as_secs() as i32).unwrap_or(0),
202 ..Default::default()
203 })
204 }
205}
206
207#[allow(non_upper_case_globals)]
208impl From<Newtype<ffi::c_uint>> for Method {
209 fn from(method: Newtype<ffi::c_uint>) -> Self {
210 match method.0 {
211 http_method_HTTP_GET => Method::Get,
212 http_method_HTTP_POST => Method::Post,
213 http_method_HTTP_DELETE => Method::Delete,
214 http_method_HTTP_HEAD => Method::Head,
215 http_method_HTTP_PUT => Method::Put,
216 http_method_HTTP_CONNECT => Method::Connect,
217 http_method_HTTP_OPTIONS => Method::Options,
218 http_method_HTTP_TRACE => Method::Trace,
219 http_method_HTTP_COPY => Method::Copy,
220 http_method_HTTP_LOCK => Method::Lock,
221 http_method_HTTP_MKCOL => Method::MkCol,
222 http_method_HTTP_MOVE => Method::Move,
223 http_method_HTTP_PROPFIND => Method::Propfind,
224 http_method_HTTP_PROPPATCH => Method::Proppatch,
225 http_method_HTTP_SEARCH => Method::Search,
226 http_method_HTTP_UNLOCK => Method::Unlock,
227 http_method_HTTP_BIND => Method::Bind,
228 http_method_HTTP_REBIND => Method::Rebind,
229 http_method_HTTP_UNBIND => Method::Unbind,
230 http_method_HTTP_ACL => Method::Acl,
231 http_method_HTTP_REPORT => Method::Report,
232 http_method_HTTP_MKACTIVITY => Method::MkActivity,
233 http_method_HTTP_CHECKOUT => Method::Checkout,
234 http_method_HTTP_MERGE => Method::Merge,
235 http_method_HTTP_MSEARCH => Method::MSearch,
236 http_method_HTTP_NOTIFY => Method::Notify,
237 http_method_HTTP_SUBSCRIBE => Method::Subscribe,
238 http_method_HTTP_UNSUBSCRIBE => Method::Unsubscribe,
239 http_method_HTTP_PATCH => Method::Patch,
240 http_method_HTTP_PURGE => Method::Purge,
241 http_method_HTTP_MKCALENDAR => Method::MkCalendar,
242 http_method_HTTP_LINK => Method::Link,
243 http_method_HTTP_UNLINK => Method::Unlink,
244 _ => unreachable!(),
245 }
246 }
247}
248
249#[cfg(esp_idf_esp_https_server_enable)]
250impl From<&Configuration> for Newtype<httpd_ssl_config_t> {
251 fn from(conf: &Configuration) -> Self {
252 let http_config: Newtype<httpd_config_t> = conf.into();
253 let transport_mode = match (conf.server_certificate, conf.private_key) {
255 (Some(_), Some(_)) => httpd_ssl_transport_mode_t_HTTPD_SSL_TRANSPORT_SECURE,
256 _ => {
257 warn!("Starting server in insecure mode because no certificates were set in the http config.");
258 httpd_ssl_transport_mode_t_HTTPD_SSL_TRANSPORT_INSECURE
259 }
260 };
261 #[allow(clippy::needless_update)]
264 Self(httpd_ssl_config_t {
265 httpd: http_config.0,
266 session_tickets: false,
267 #[cfg(not(esp_idf_version_major = "4"))]
268 use_secure_element: false,
269 port_secure: conf.https_port,
270 port_insecure: conf.http_port,
271 transport_mode,
272 cacert_pem: ptr::null(),
273 cacert_len: 0,
274 prvtkey_pem: ptr::null(),
275 prvtkey_len: 0,
276 #[cfg(esp_idf_version_major = "4")]
277 client_verify_cert_pem: ptr::null(),
278 #[cfg(esp_idf_version_major = "4")]
279 client_verify_cert_len: 0,
280 #[cfg(not(esp_idf_version_major = "4"))]
281 servercert: ptr::null(),
282 #[cfg(not(esp_idf_version_major = "4"))]
283 servercert_len: 0,
284 user_cb: None,
285 ..Default::default()
286 })
287 }
288}
289
290impl From<Method> for Newtype<ffi::c_uint> {
291 fn from(method: Method) -> Self {
292 Self(match method {
293 Method::Get => http_method_HTTP_GET,
294 Method::Post => http_method_HTTP_POST,
295 Method::Delete => http_method_HTTP_DELETE,
296 Method::Head => http_method_HTTP_HEAD,
297 Method::Put => http_method_HTTP_PUT,
298 Method::Connect => http_method_HTTP_CONNECT,
299 Method::Options => http_method_HTTP_OPTIONS,
300 Method::Trace => http_method_HTTP_TRACE,
301 Method::Copy => http_method_HTTP_COPY,
302 Method::Lock => http_method_HTTP_LOCK,
303 Method::MkCol => http_method_HTTP_MKCOL,
304 Method::Move => http_method_HTTP_MOVE,
305 Method::Propfind => http_method_HTTP_PROPFIND,
306 Method::Proppatch => http_method_HTTP_PROPPATCH,
307 Method::Search => http_method_HTTP_SEARCH,
308 Method::Unlock => http_method_HTTP_UNLOCK,
309 Method::Bind => http_method_HTTP_BIND,
310 Method::Rebind => http_method_HTTP_REBIND,
311 Method::Unbind => http_method_HTTP_UNBIND,
312 Method::Acl => http_method_HTTP_ACL,
313 Method::Report => http_method_HTTP_REPORT,
314 Method::MkActivity => http_method_HTTP_MKACTIVITY,
315 Method::Checkout => http_method_HTTP_CHECKOUT,
316 Method::Merge => http_method_HTTP_MERGE,
317 Method::MSearch => http_method_HTTP_MSEARCH,
318 Method::Notify => http_method_HTTP_NOTIFY,
319 Method::Subscribe => http_method_HTTP_SUBSCRIBE,
320 Method::Unsubscribe => http_method_HTTP_UNSUBSCRIBE,
321 Method::Patch => http_method_HTTP_PATCH,
322 Method::Purge => http_method_HTTP_PURGE,
323 Method::MkCalendar => http_method_HTTP_MKCALENDAR,
324 Method::Link => http_method_HTTP_LINK,
325 Method::Unlink => http_method_HTTP_UNLINK,
326 })
327 }
328}
329
330static OPEN_SESSIONS: Mutex<BTreeMap<(u32, ffi::c_int), Arc<AtomicBool>>> =
331 Mutex::new(BTreeMap::new());
332static CLOSE_HANDLERS: Mutex<BTreeMap<u32, Vec<CloseHandler<'static>>>> =
333 Mutex::new(BTreeMap::new());
334
335type NativeHandler<'a> = Box<dyn Fn(*mut httpd_req_t) -> ffi::c_int + 'a>;
336type CloseHandler<'a> = Box<dyn Fn(ffi::c_int) + Send + 'a>;
337
338pub struct EspHttpServer<'a> {
339 sd: httpd_handle_t,
340 registrations: Vec<(CString, crate::sys::httpd_uri_t)>,
341 _reg: PhantomData<&'a ()>,
342}
343
344impl EspHttpServer<'static> {
345 pub fn new(conf: &Configuration) -> Result<Self, EspIOError> {
346 Self::internal_new(conf)
347 }
348}
349
350impl<'a> EspHttpServer<'a> {
352 pub unsafe fn new_nonstatic(conf: &Configuration) -> Result<Self, EspIOError> {
376 Self::internal_new(conf)
377 }
378
379 fn internal_new(conf: &Configuration) -> Result<Self, EspIOError> {
380 let mut handle: httpd_handle_t = ptr::null_mut();
381 let handle_ref = &mut handle;
382
383 #[cfg(not(esp_idf_esp_https_server_enable))]
384 {
385 let mut config: Newtype<httpd_config_t> = conf.into();
386 config.0.close_fn = Some(Self::close_fn);
387 esp!(unsafe { httpd_start(handle_ref, &config.0 as *const _) })?;
388 }
389
390 #[cfg(esp_idf_esp_https_server_enable)]
391 {
392 let mut config: Newtype<httpd_ssl_config_t> = conf.into();
393 config.0.httpd.close_fn = Some(Self::close_fn);
394
395 if let (Some(cert), Some(private_key)) = (conf.server_certificate, conf.private_key) {
396 #[cfg(esp_idf_version_major = "4")]
402 {
403 config.0.cacert_pem = cert.as_esp_idf_raw_ptr() as _;
404 config.0.cacert_len = cert.as_esp_idf_raw_len();
405 }
406
407 #[cfg(not(esp_idf_version_major = "4"))]
408 {
409 config.0.servercert = cert.as_esp_idf_raw_ptr() as _;
410 config.0.servercert_len = cert.as_esp_idf_raw_len();
411 }
412
413 config.0.prvtkey_pem = private_key.as_esp_idf_raw_ptr() as _;
414 config.0.prvtkey_len = private_key.as_esp_idf_raw_len();
415
416 esp!(unsafe { httpd_ssl_start(handle_ref, &mut config.0) })?;
417 } else {
418 esp!(unsafe { httpd_ssl_start(handle_ref, &mut config.0) })?;
419 }
420 }
421
422 info!("Started Httpd server with config {conf:?}");
423
424 let server = Self {
425 sd: handle,
426 registrations: Vec::new(),
427 _reg: PhantomData,
428 };
429
430 CLOSE_HANDLERS.lock().insert(server.sd as _, Vec::new());
431
432 Ok(server)
433 }
434
435 fn unregister(&mut self, uri: CString, conf: httpd_uri_t) -> Result<(), EspIOError> {
437 unsafe {
438 esp!(httpd_unregister_uri_handler(
439 self.sd,
440 uri.as_ptr() as _,
441 conf.method
442 ))?;
443
444 let _drop = Box::from_raw(conf.user_ctx as *mut NativeHandler<'static>);
445 };
446
447 info!(
448 "Unregistered Httpd server handler {:?} for URI \"{}\"",
449 conf.method,
450 uri.to_str().unwrap()
451 );
452
453 Ok(())
454 }
455
456 fn stop(&mut self) -> Result<(), EspIOError> {
458 if !self.sd.is_null() {
459 while let Some((uri, registration)) = self.registrations.pop() {
460 self.unregister(uri, registration)?;
461 }
462
463 #[cfg(not(esp_idf_esp_https_server_enable))]
466 esp!(unsafe { crate::sys::httpd_stop(self.sd) })?;
467
468 #[cfg(all(esp_idf_esp_https_server_enable, esp_idf_version_major = "4"))]
470 unsafe {
471 crate::sys::httpd_ssl_stop(self.sd)
472 };
473
474 #[cfg(all(esp_idf_esp_https_server_enable, not(esp_idf_version_major = "4")))]
476 esp!(unsafe { crate::sys::httpd_ssl_stop(self.sd) })?;
477
478 CLOSE_HANDLERS.lock().remove(&(self.sd as u32));
479
480 self.sd = ptr::null_mut();
481 }
482
483 info!("Httpd server stopped");
484
485 Ok(())
486 }
487
488 pub fn handler_chain<C>(&mut self, chain: C) -> Result<&mut Self, EspError>
489 where
490 C: EspHttpTraversableChain<'a>,
491 {
492 chain.accept(self)?;
493
494 Ok(self)
495 }
496
497 pub unsafe fn handler_chain_nonstatic<C>(&mut self, chain: C) -> Result<&mut Self, EspError>
521 where
522 C: EspHttpTraversableChainNonstatic<'a>,
523 {
524 chain.accept(self)?;
525
526 Ok(self)
527 }
528
529 pub fn handler<H>(
531 &mut self,
532 uri: &str,
533 method: Method,
534 handler: H,
535 ) -> Result<&mut Self, EspError>
536 where
537 H: for<'r> Handler<EspHttpConnection<'r>> + Send + 'static,
538 {
539 unsafe { self.handler_nonstatic(uri, method, handler) }
540 }
541
542 pub unsafe fn handler_nonstatic<H>(
568 &mut self,
569 uri: &str,
570 method: Method,
571 handler: H,
572 ) -> Result<&mut Self, EspError>
573 where
574 H: for<'r> Handler<EspHttpConnection<'r>> + Send + 'a,
575 {
576 let c_str = to_cstring_arg(uri)?;
577
578 #[allow(clippy::needless_update)]
579 let conf = httpd_uri_t {
580 uri: c_str.as_ptr() as _,
581 method: Newtype::<ffi::c_uint>::from(method).0,
582 user_ctx: Box::into_raw(Box::new(self.to_native_handler(handler))) as *mut _,
583 handler: Some(EspHttpServer::handle_req),
584 ..Default::default()
585 };
586
587 esp!(unsafe { crate::sys::httpd_register_uri_handler(self.sd, &conf) })?;
588
589 info!(
590 "Registered Httpd server handler {:?} for URI \"{}\"",
591 method,
592 c_str.to_str().unwrap()
593 );
594
595 self.registrations.push((c_str, conf));
596
597 Ok(self)
598 }
599
600 pub fn fn_handler<E, F>(
606 &mut self,
607 uri: &str,
608 method: Method,
609 f: F,
610 ) -> Result<&mut Self, EspError>
611 where
612 F: for<'r> Fn(Request<&mut EspHttpConnection<'r>>) -> Result<(), E> + Send + 'static,
613 E: Debug,
614 {
615 unsafe { self.fn_handler_nonstatic(uri, method, f) }
616 }
617
618 pub unsafe fn fn_handler_nonstatic<E, F>(
648 &mut self,
649 uri: &str,
650 method: Method,
651 f: F,
652 ) -> Result<&mut Self, EspError>
653 where
654 F: for<'r> Fn(Request<&mut EspHttpConnection<'r>>) -> Result<(), E> + Send + 'a,
655 E: Debug,
656 {
657 self.handler_nonstatic(uri, method, FnHandler::new(f))
658 }
659
660 fn to_native_handler<H>(&self, handler: H) -> NativeHandler<'a>
661 where
662 H: Handler<EspHttpConnection<'a>> + Send + 'a,
663 {
664 Box::new(move |raw_req| {
665 let mut connection = EspHttpConnection::new(unsafe { raw_req.as_mut().unwrap() });
666
667 let result = connection.invoke(&handler);
668
669 match result {
670 Ok(()) => {
671 if let Err(e) = connection.complete() {
672 connection.handle_error(e);
673 }
674 }
675 Err(e) => {
676 connection.handle_error(e);
677 if let Err(e) = connection.complete() {
678 connection.handle_error(e);
679 }
680 }
681 }
682
683 ESP_OK as _
684 })
685 }
686
687 extern "C" fn handle_req(raw_req: *mut httpd_req_t) -> ffi::c_int {
688 let handler_ptr = (unsafe { *raw_req }).user_ctx as *mut NativeHandler<'static>;
689
690 let handler = unsafe { handler_ptr.as_ref() }.unwrap();
691
692 (handler)(raw_req)
693 }
694
695 extern "C" fn close_fn(sd: httpd_handle_t, sockfd: ffi::c_int) {
696 {
697 let mut sessions = OPEN_SESSIONS.lock();
698
699 if let Some(closed) = sessions.remove(&(sd as u32, sockfd)) {
700 closed.store(true, Ordering::SeqCst);
701 }
702 }
703
704 let all_close_handlers = CLOSE_HANDLERS.lock();
705
706 let close_handlers = all_close_handlers.get(&(sd as u32)).unwrap();
707
708 for close_handler in close_handlers {
709 (close_handler)(sockfd);
710 }
711 esp_nofail!(unsafe { close(sockfd) });
712 }
713}
714
715impl Drop for EspHttpServer<'_> {
716 fn drop(&mut self) {
717 self.stop().expect("Unable to stop the server cleanly");
718 }
719}
720
721impl RawHandle for EspHttpServer<'_> {
722 type Handle = httpd_handle_t;
723
724 fn handle(&self) -> Self::Handle {
725 self.sd
726 }
727}
728
729pub fn fn_handler<F, E>(f: F) -> FnHandler<F>
733where
734 F: for<'a> Fn(Request<&mut EspHttpConnection<'a>>) -> Result<(), E> + Send,
735 E: Debug,
736{
737 FnHandler::new(f)
738}
739
740pub trait EspHttpTraversableChain<'a> {
741 fn accept(self, server: &mut EspHttpServer<'a>) -> Result<(), EspError>;
742}
743
744pub unsafe trait EspHttpTraversableChainNonstatic<'a>: EspHttpTraversableChain<'a> {}
752
753impl<'a> EspHttpTraversableChain<'a> for ChainRoot {
754 fn accept(self, _server: &mut EspHttpServer<'a>) -> Result<(), EspError> {
755 Ok(())
756 }
757}
758
759impl<'a, H, N> EspHttpTraversableChain<'a> for ChainHandler<H, N>
760where
761 H: for<'r> Handler<EspHttpConnection<'r>> + Send + 'static,
762 N: EspHttpTraversableChain<'a>,
763{
764 fn accept(self, server: &mut EspHttpServer<'a>) -> Result<(), EspError> {
765 self.next.accept(server)?;
766
767 server.handler(self.path, self.method, self.handler)?;
768
769 Ok(())
770 }
771}
772
773pub struct NonstaticChain<H, N>(ChainHandler<H, N>);
777
778impl<H, N> NonstaticChain<H, N> {
779 pub fn new(handler: ChainHandler<H, N>) -> Self {
781 Self(handler)
782 }
783}
784
785unsafe impl EspHttpTraversableChainNonstatic<'_> for ChainRoot {}
786
787impl<'a, H, N> EspHttpTraversableChain<'a> for NonstaticChain<H, N>
788where
789 H: for<'r> Handler<EspHttpConnection<'r>> + Send + 'a,
790 N: EspHttpTraversableChain<'a>,
791{
792 fn accept(self, server: &mut EspHttpServer<'a>) -> Result<(), EspError> {
793 self.0.next.accept(server)?;
794
795 unsafe {
796 server.handler_nonstatic(self.0.path, self.0.method, self.0.handler)?;
797 }
798
799 Ok(())
800 }
801}
802
803unsafe impl<'a, H, N> EspHttpTraversableChainNonstatic<'a> for NonstaticChain<H, N>
804where
805 H: for<'r> Handler<EspHttpConnection<'r>> + Send + 'a,
806 N: EspHttpTraversableChain<'a>,
807{
808}
809
810pub struct EspHttpRawConnection<'a>(&'a mut httpd_req_t);
811
812impl EspHttpRawConnection<'_> {
813 pub fn read(&mut self, buf: &mut [u8]) -> Result<usize, EspError> {
814 if !buf.is_empty() {
815 let fd = unsafe { httpd_req_to_sockfd(self.0) };
816 let len = unsafe { crate::sys::read(fd, buf.as_mut_ptr() as *mut _, buf.len()) };
817
818 Ok(len as _)
819 } else {
820 Ok(0)
821 }
822 }
823
824 pub fn write(&mut self, buf: &[u8]) -> Result<usize, EspError> {
825 if !buf.is_empty() {
826 let fd = unsafe { httpd_req_to_sockfd(self.0) };
827 let len = unsafe { crate::sys::write(fd, buf.as_ptr() as *const _, buf.len()) };
828
829 Ok(len as _)
830 } else {
831 Ok(0)
832 }
833 }
834
835 pub fn write_all(&mut self, data: &[u8]) -> Result<(), EspError> {
836 let mut offset = 0;
837
838 while offset < data.len() {
839 offset += self.write(&data[offset..])?;
840 }
841
842 Ok(())
843 }
844
845 #[cfg(esp_idf_lwip_ipv4)]
849 pub fn source_ipv4(&self) -> Result<Ipv4Addr, EspError> {
850 unsafe {
851 let sockfd = httpd_req_to_sockfd(self.handle());
852
853 if sockfd == -1 {
854 return Err(EspError::from_infallible::<ESP_FAIL>());
855 }
856
857 let mut addr = sockaddr_in {
858 sin_len: core::mem::size_of::<sockaddr_in>() as _,
859 sin_family: AF_INET as _,
860 ..Default::default()
861 };
862
863 esp!(lwip_getpeername(
864 sockfd,
865 &mut addr as *mut _ as *mut _,
866 &mut core::mem::size_of::<sockaddr_in>() as *mut _ as *mut _,
867 ))?;
868
869 Ok(Ipv4Addr::from(u32::from_be(addr.sin_addr.s_addr)))
870 }
871 }
872
873 #[cfg(esp_idf_lwip_ipv6)]
877 pub fn source_ipv6(&self) -> Result<Ipv6Addr, EspError> {
878 unsafe {
879 let sockfd = httpd_req_to_sockfd(self.handle());
880
881 if sockfd == -1 {
882 return Err(EspError::from_infallible::<ESP_FAIL>());
883 }
884
885 let mut addr = sockaddr_in6 {
886 sin6_len: core::mem::size_of::<sockaddr_in6>() as _,
887 sin6_family: AF_INET6 as _,
888 ..Default::default()
889 };
890
891 esp!(lwip_getpeername(
892 sockfd,
893 &mut addr as *mut _ as *mut _,
894 &mut core::mem::size_of::<sockaddr_in6>() as *mut _ as *mut _,
895 ))?;
896
897 Ok(Ipv6Addr::from(addr.sin6_addr.un.u8_addr))
898 }
899 }
900}
901
902impl RawHandle for EspHttpRawConnection<'_> {
903 type Handle = *mut httpd_req_t;
904
905 fn handle(&self) -> Self::Handle {
906 self.0 as *const _ as *mut _
907 }
908}
909
910impl ErrorType for EspHttpRawConnection<'_> {
911 type Error = EspIOError;
912}
913
914impl Read for EspHttpRawConnection<'_> {
915 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
916 EspHttpRawConnection::read(self, buf).map_err(EspIOError)
917 }
918}
919
920impl Write for EspHttpRawConnection<'_> {
921 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
922 EspHttpRawConnection::write(self, buf).map_err(EspIOError)
923 }
924
925 fn flush(&mut self) -> Result<(), Self::Error> {
926 Ok(())
927 }
928}
929
930type EspHttpHeaders = BTreeMap<Uncased<'static>, String>;
931
932pub struct EspHttpConnection<'a> {
933 request: EspHttpRawConnection<'a>,
934 headers: Option<UnsafeCell<EspHttpHeaders>>,
935 response_headers: Option<Vec<CString>>,
936}
937
938impl<'a> EspHttpConnection<'a> {
940 fn new(raw_req: &'a mut httpd_req_t) -> Self {
941 Self {
942 request: EspHttpRawConnection(raw_req),
943 headers: Some(UnsafeCell::new(EspHttpHeaders::new())),
944 response_headers: None,
945 }
946 }
947
948 pub fn uri(&self) -> &str {
950 self.assert_request();
951
952 let c_uri = unsafe { CStr::from_ptr(self.request.0.uri.as_ptr()) };
953
954 c_uri.to_str().unwrap()
955 }
956
957 pub fn method(&self) -> Method {
959 self.assert_request();
960
961 Method::from(Newtype(self.request.0.method as u32))
962 }
963
964 pub fn header(&self, name: &str) -> Option<&str> {
966 self.assert_request();
967
968 let headers = self.headers.as_ref().unwrap();
969
970 if let Some(value) = unsafe { headers.get().as_ref().unwrap() }.get(UncasedStr::new(name)) {
971 Some(value.as_ref())
972 } else {
973 let raw_req = self.request.0 as *const httpd_req_t as *mut httpd_req_t;
974
975 if let Ok(c_name) = to_cstring_arg(name) {
976 match unsafe { httpd_req_get_hdr_value_len(raw_req, c_name.as_ptr() as _) } {
977 0 => None,
978 len => {
979 let mut buf: Vec<u8> = Vec::with_capacity(len + 1);
985
986 esp_nofail!(unsafe {
987 httpd_req_get_hdr_value_str(
988 raw_req,
989 c_name.as_ptr(),
990 buf.as_mut_ptr().cast(),
991 len + 1,
992 )
993 });
994
995 unsafe {
996 buf.set_len(len + 1);
997 }
998
999 let value = String::from_utf8_lossy(&buf[..len]).into_owned();
1001 unsafe { headers.get().as_mut().unwrap() }
1002 .insert(Uncased::from(name.to_owned()), value);
1003
1004 unsafe { headers.get().as_ref().unwrap() }
1005 .get(UncasedStr::new(name))
1006 .map(|s| s.as_ref())
1007 }
1008 }
1009 } else {
1010 None
1011 }
1012 }
1013 }
1014
1015 pub fn split(&mut self) -> (&EspHttpConnection<'a>, &mut Self) {
1016 self.assert_request();
1017
1018 let headers_ptr: *const EspHttpConnection<'a> = self as *const _;
1019
1020 let headers = unsafe { headers_ptr.as_ref().unwrap() };
1021
1022 (headers, self)
1023 }
1024
1025 pub fn initiate_response(
1028 &mut self,
1029 status: u16,
1030 message: Option<&str>,
1031 headers: &[(&str, &str)],
1032 ) -> Result<(), EspError> {
1033 self.assert_request();
1034
1035 let mut c_headers = Vec::new();
1036
1037 let status = if let Some(message) = message {
1038 format!("{status} {message}")
1039 } else {
1040 status.to_string()
1041 };
1042
1043 let c_status = to_cstring_arg(status.as_str())?;
1044 esp!(unsafe { httpd_resp_set_status(self.request.0, c_status.as_ptr() as _) })?;
1045
1046 c_headers.push(c_status);
1047
1048 for (key, value) in headers {
1049 if key.eq_ignore_ascii_case("Content-Type") {
1050 let c_type = to_cstring_arg(value)?;
1051
1052 esp!(unsafe { httpd_resp_set_type(self.request.0, c_type.as_c_str().as_ptr()) })?;
1053
1054 c_headers.push(c_type);
1055 } else if key.eq_ignore_ascii_case("Content-Length") {
1056 let c_len = to_cstring_arg(value)?;
1057
1058 c_headers.push(c_len);
1061 } else {
1062 let name = to_cstring_arg(key)?;
1063 let value = to_cstring_arg(value)?;
1064
1065 esp!(unsafe {
1066 httpd_resp_set_hdr(
1067 self.request.0,
1068 name.as_c_str().as_ptr() as _,
1069 value.as_c_str().as_ptr() as _,
1070 )
1071 })?;
1072
1073 c_headers.push(name);
1074 c_headers.push(value);
1075 }
1076 }
1077
1078 self.response_headers = Some(c_headers);
1079 self.headers = None;
1080
1081 Ok(())
1082 }
1083
1084 pub fn is_response_initiated(&self) -> bool {
1086 self.headers.is_none()
1087 }
1088
1089 pub fn read(&mut self, buf: &mut [u8]) -> Result<usize, EspError> {
1107 self.assert_request();
1108
1109 unsafe {
1110 let len = httpd_req_recv(self.request.0, buf.as_mut_ptr() as *mut _, buf.len());
1111
1112 if len < 0 {
1113 esp!(len)?;
1114 }
1115
1116 Ok(len as usize)
1117 }
1118 }
1119
1120 pub fn write(&mut self, buf: &[u8]) -> Result<usize, EspError> {
1122 self.assert_response();
1123
1124 if !buf.is_empty() {
1125 esp!(unsafe {
1126 httpd_resp_send_chunk(self.request.0, buf.as_ptr().cast(), buf.len() as isize)
1127 })?;
1128
1129 self.response_headers = None;
1130 }
1131
1132 Ok(buf.len())
1133 }
1134
1135 pub fn write_all(&mut self, buf: &[u8]) -> Result<(), EspError> {
1138 self.write(buf)?;
1139
1140 Ok(())
1141 }
1142
1143 pub fn raw_connection(&mut self) -> Result<&mut EspHttpRawConnection<'a>, EspError> {
1144 Ok(&mut self.request)
1145 }
1146
1147 fn invoke<H>(&mut self, handler: &H) -> Result<(), H::Error>
1148 where
1149 H: Handler<Self>,
1150 {
1151 handler.handle(self)?;
1154
1155 Ok(())
1156 }
1157
1158 fn complete(&mut self) -> Result<(), EspError> {
1159 let buf = &[];
1160
1161 if self.response_headers.is_some() {
1162 esp!(unsafe { httpd_resp_send(self.request.0, buf.as_ptr() as *const _, 0) })?;
1163 } else {
1164 esp!(unsafe { httpd_resp_send_chunk(self.request.0, buf.as_ptr() as *const _, 0) })?;
1165 }
1166
1167 self.response_headers = None;
1168
1169 Ok(())
1170 }
1171
1172 fn handle_error<E>(&mut self, error: E)
1173 where
1174 E: Debug,
1175 {
1176 if self.headers.is_some() {
1177 info!("About to handle internal error [{error:?}], response not sent yet");
1178
1179 if let Err(error2) = self.render_error(&error) {
1180 warn!(
1181 "Internal error[{error2}] while rendering another internal error:\n{error:?}"
1182 );
1183 }
1184 } else {
1185 warn!("Unhandled internal error [{error:?}], response is already sent");
1186 }
1187 }
1188
1189 fn render_error<E>(&mut self, error: E) -> Result<(), EspError>
1190 where
1191 E: Debug,
1192 {
1193 self.initiate_response(500, Some("Internal Error"), &[content_type("text/html")])?;
1194
1195 self.write_all(
1196 format!(
1197 r#"
1198 <!DOCTYPE html5>
1199 <html>
1200 <body style="font-family: Verdana, Sans;">
1201 <h1>INTERNAL ERROR</h1>
1202 <hr>
1203 <pre>{error:?}</pre>
1204 <body>
1205 </html>
1206 "#
1207 )
1208 .as_bytes(),
1209 )?;
1210
1211 Ok(())
1212 }
1213
1214 fn assert_request(&self) {
1215 if self.headers.is_none() {
1216 panic!("connection is not in request phase");
1217 }
1218 }
1219
1220 fn assert_response(&self) {
1221 if self.headers.is_some() {
1222 panic!("connection is not in response phase");
1223 }
1224 }
1225}
1226
1227impl RawHandle for EspHttpConnection<'_> {
1228 type Handle = *mut httpd_req_t;
1229
1230 fn handle(&self) -> Self::Handle {
1231 self.request.handle()
1232 }
1233}
1234
1235impl embedded_svc::http::Query for EspHttpConnection<'_> {
1236 fn uri(&self) -> &str {
1237 EspHttpConnection::uri(self)
1238 }
1239
1240 fn method(&self) -> Method {
1241 EspHttpConnection::method(self)
1242 }
1243}
1244
1245impl embedded_svc::http::Headers for EspHttpConnection<'_> {
1246 fn header(&self, name: &str) -> Option<&str> {
1247 EspHttpConnection::header(self, name)
1248 }
1249}
1250
1251impl ErrorType for EspHttpConnection<'_> {
1252 type Error = EspIOError;
1253}
1254
1255impl Read for EspHttpConnection<'_> {
1256 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
1257 EspHttpConnection::read(self, buf).map_err(EspIOError)
1258 }
1259}
1260
1261impl Write for EspHttpConnection<'_> {
1262 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
1263 EspHttpConnection::write(self, buf).map_err(EspIOError)
1264 }
1265
1266 fn flush(&mut self) -> Result<(), Self::Error> {
1267 self.assert_response();
1268
1269 Ok(())
1270 }
1271}
1272
1273impl<'b> Connection for EspHttpConnection<'b> {
1274 type Headers = Self;
1275
1276 type Read = Self;
1277
1278 type RawConnectionError = EspIOError;
1279
1280 type RawConnection = EspHttpRawConnection<'b>;
1281
1282 fn split(&mut self) -> (&Self::Headers, &mut Self::Read) {
1283 EspHttpConnection::split(self)
1284 }
1285
1286 fn initiate_response<'a>(
1287 &'a mut self,
1288 status: u16,
1289 message: Option<&'a str>,
1290 headers: &'a [(&'a str, &'a str)],
1291 ) -> Result<(), Self::Error> {
1292 EspHttpConnection::initiate_response(self, status, message, headers).map_err(EspIOError)
1293 }
1294
1295 fn is_response_initiated(&self) -> bool {
1296 EspHttpConnection::is_response_initiated(self)
1297 }
1298
1299 fn raw_connection(&mut self) -> Result<&mut Self::RawConnection, Self::Error> {
1300 EspHttpConnection::raw_connection(self).map_err(EspIOError)
1301 }
1302}
1303
1304#[cfg(esp_idf_httpd_ws_support)]
1305pub mod ws {
1306 use core::ffi;
1307 use core::fmt::Debug;
1308 use core::sync::atomic::{AtomicBool, Ordering};
1309
1310 extern crate alloc;
1311 use alloc::boxed::Box;
1312 use alloc::sync::Arc;
1313
1314 use ::log::*;
1315
1316 use embedded_svc::http::Method;
1317 use embedded_svc::ws::*;
1318
1319 use crate::sys::*;
1320
1321 use crate::private::common::Newtype;
1322 use crate::private::cstr::to_cstring_arg;
1323 use crate::private::mutex::{Condvar, Mutex};
1324
1325 use super::EspHttpServer;
1326 use super::CLOSE_HANDLERS;
1327 use super::OPEN_SESSIONS;
1328 use super::{CloseHandler, NativeHandler};
1329
1330 pub enum EspHttpWsConnection {
1332 New(httpd_handle_t, *mut httpd_req_t),
1333 Receiving(httpd_handle_t, *mut httpd_req_t, Option<httpd_ws_frame_t>),
1334 Closed(ffi::c_int),
1335 }
1336
1337 impl EspHttpWsConnection {
1338 pub fn session(&self) -> i32 {
1340 match self {
1341 Self::New(_, raw_req) | Self::Receiving(_, raw_req, _) => unsafe {
1342 httpd_req_to_sockfd(*raw_req)
1343 },
1344 Self::Closed(fd) => *fd,
1345 }
1346 }
1347
1348 pub fn is_new(&self) -> bool {
1350 matches!(self, Self::New(_, _))
1351 }
1352
1353 pub fn is_closed(&self) -> bool {
1355 matches!(self, Self::Closed(_))
1356 }
1357
1358 pub fn create_detached_sender(&self) -> Result<EspHttpWsDetachedSender, EspError> {
1359 match self {
1360 Self::New(sd, raw_req) | Self::Receiving(sd, raw_req, _) => {
1361 let fd = unsafe { httpd_req_to_sockfd(*raw_req) };
1362
1363 let mut sessions = OPEN_SESSIONS.lock();
1364
1365 let closed = sessions
1366 .entry((*sd as u32, fd))
1367 .or_insert_with(|| Arc::new(AtomicBool::new(false)));
1368
1369 Ok(EspHttpWsDetachedSender::new(*sd, fd, closed.clone()))
1370 }
1371 Self::Closed(_) => Err(EspError::from_infallible::<ESP_FAIL>()),
1372 }
1373 }
1374
1375 pub fn send(&mut self, frame_type: FrameType, frame_data: &[u8]) -> Result<(), EspError> {
1377 match self {
1378 Self::New(_, raw_req) | Self::Receiving(_, raw_req, _) => {
1379 let raw_frame = Self::create_raw_frame(frame_type, frame_data);
1380
1381 esp!(unsafe {
1382 httpd_ws_send_frame(*raw_req, &raw_frame as *const _ as *mut _)
1383 })?;
1384
1385 Ok(())
1386 }
1387 _ => Err(EspError::from_infallible::<ESP_FAIL>()),
1388 }
1389 }
1390
1391 pub fn recv(&mut self, frame_data_buf: &mut [u8]) -> Result<(FrameType, usize), EspError> {
1393 match self {
1394 Self::New(_, _) => Err(EspError::from_infallible::<ESP_FAIL>()),
1395 Self::Receiving(_, raw_req, ref mut raw_frame_mut) => {
1396 let raw_frame = loop {
1397 if let Some(raw_frame) = raw_frame_mut.as_mut() {
1398 break raw_frame;
1399 }
1400
1401 let mut raw_frame: httpd_ws_frame_t = Default::default();
1402
1403 esp!(unsafe {
1404 httpd_ws_recv_frame(*raw_req, &mut raw_frame as *mut _, 0)
1405 })?;
1406
1407 *raw_frame_mut = Some(raw_frame);
1416 };
1417
1418 let (frame_type, len) = Self::create_frame_type(raw_frame);
1419
1420 if frame_data_buf.len() >= len {
1421 raw_frame.payload = frame_data_buf.as_mut_ptr() as *mut _;
1422 esp!(unsafe { httpd_ws_recv_frame(*raw_req, raw_frame as *mut _, len) })?;
1423
1424 *raw_frame_mut = None;
1425 }
1426
1427 Ok((frame_type, len))
1428 }
1429 Self::Closed(_) => Ok((FrameType::SocketClose, 0)),
1430 }
1431 }
1432
1433 #[allow(clippy::needless_update)]
1434 fn create_raw_frame(frame_type: FrameType, frame_data: &[u8]) -> httpd_ws_frame_t {
1435 httpd_ws_frame_t {
1436 type_: match frame_type {
1437 FrameType::Text(_) => httpd_ws_type_t_HTTPD_WS_TYPE_TEXT,
1438 FrameType::Binary(_) => httpd_ws_type_t_HTTPD_WS_TYPE_BINARY,
1439 FrameType::Ping => httpd_ws_type_t_HTTPD_WS_TYPE_PING,
1440 FrameType::Pong => httpd_ws_type_t_HTTPD_WS_TYPE_PONG,
1441 FrameType::Close => httpd_ws_type_t_HTTPD_WS_TYPE_CLOSE,
1442 FrameType::Continue(_) => httpd_ws_type_t_HTTPD_WS_TYPE_CONTINUE,
1443 FrameType::SocketClose => panic!("Cannot send SocketClose as a frame"),
1444 },
1445 final_: frame_type.is_final(),
1446 fragmented: frame_type.is_fragmented(),
1447 payload: frame_data.as_ptr() as *const _ as *mut _,
1448 len: frame_data.len(),
1449 ..Default::default()
1450 }
1451 }
1452
1453 #[allow(non_upper_case_globals)]
1454 fn create_frame_type(raw_frame: &httpd_ws_frame_t) -> (FrameType, usize) {
1455 match raw_frame.type_ {
1456 httpd_ws_type_t_HTTPD_WS_TYPE_TEXT => {
1457 (FrameType::Text(raw_frame.fragmented), raw_frame.len + 1)
1458 }
1459 httpd_ws_type_t_HTTPD_WS_TYPE_BINARY => {
1460 (FrameType::Binary(raw_frame.fragmented), raw_frame.len)
1461 }
1462 httpd_ws_type_t_HTTPD_WS_TYPE_CONTINUE => {
1463 (FrameType::Continue(raw_frame.final_), raw_frame.len)
1464 }
1465 httpd_ws_type_t_HTTPD_WS_TYPE_PING => (FrameType::Ping, 0),
1466 httpd_ws_type_t_HTTPD_WS_TYPE_PONG => (FrameType::Pong, 0),
1467 httpd_ws_type_t_HTTPD_WS_TYPE_CLOSE => (FrameType::Close, 0),
1468 _ => panic!("Unknown frame type: {}", raw_frame.type_),
1469 }
1470 }
1471 }
1472
1473 impl ErrorType for EspHttpWsConnection {
1474 type Error = EspError;
1475 }
1476
1477 impl Sender for EspHttpWsConnection {
1478 fn send(&mut self, frame_type: FrameType, frame_data: &[u8]) -> Result<(), Self::Error> {
1479 EspHttpWsConnection::send(self, frame_type, frame_data)
1480 }
1481 }
1482
1483 impl Receiver for EspHttpWsConnection {
1484 fn recv(&mut self, frame_data_buf: &mut [u8]) -> Result<(FrameType, usize), Self::Error> {
1485 EspHttpWsConnection::recv(self, frame_data_buf)
1486 }
1487 }
1488
1489 struct EspWsDetachedSendRequest {
1490 sd: httpd_handle_t,
1491 fd: ffi::c_int,
1492
1493 closed: Arc<AtomicBool>,
1494
1495 raw_frame: *const httpd_ws_frame_t,
1496
1497 error_code: Mutex<Option<u32>>,
1498 condvar: Condvar,
1499 }
1500
1501 pub struct EspHttpWsDetachedSender {
1502 sd: httpd_handle_t,
1503 fd: ffi::c_int,
1504 closed: Arc<AtomicBool>,
1505 }
1506
1507 impl EspHttpWsDetachedSender {
1508 fn new(sd: httpd_handle_t, fd: ffi::c_int, closed: Arc<AtomicBool>) -> Self {
1509 Self { sd, fd, closed }
1510 }
1511
1512 pub fn session(&self) -> i32 {
1513 self.fd
1514 }
1515
1516 pub fn is_new(&self) -> bool {
1517 false
1518 }
1519
1520 pub fn is_closed(&self) -> bool {
1521 self.closed.load(Ordering::SeqCst)
1522 }
1523
1524 pub fn send(&mut self, frame_type: FrameType, frame_data: &[u8]) -> Result<(), EspError> {
1525 if !self.closed.load(Ordering::SeqCst) {
1526 let raw_frame = EspHttpWsConnection::create_raw_frame(frame_type, frame_data);
1527
1528 let send_request = EspWsDetachedSendRequest {
1529 sd: self.sd,
1530 fd: self.fd,
1531
1532 closed: self.closed.clone(),
1533
1534 raw_frame: &raw_frame as *const _,
1535
1536 error_code: Mutex::new(None),
1537 condvar: Condvar::new(),
1538 };
1539
1540 esp!(unsafe {
1541 httpd_queue_work(
1542 self.sd,
1543 Some(Self::enqueue),
1544 &send_request as *const _ as *mut _,
1545 )
1546 })?;
1547
1548 let mut guard = send_request.error_code.lock();
1549
1550 while guard.is_none() {
1551 guard = send_request.condvar.wait(guard);
1552 }
1553
1554 esp!((*guard).unwrap())?;
1555 } else {
1556 return Err(EspError::from_infallible::<ESP_FAIL>());
1557 }
1558
1559 Ok(())
1560 }
1561
1562 extern "C" fn enqueue(arg: *mut ffi::c_void) {
1563 let request = unsafe { (arg as *const EspWsDetachedSendRequest).as_ref().unwrap() };
1564
1565 let ret = if !request.closed.load(Ordering::SeqCst) {
1566 unsafe {
1567 httpd_ws_send_frame_async(
1568 request.sd,
1569 request.fd,
1570 request.raw_frame as *const _ as *mut _,
1571 )
1572 }
1573 } else {
1574 ESP_FAIL
1575 };
1576
1577 let mut guard = request.error_code.lock();
1578
1579 *guard = Some(ret as _);
1580
1581 request.condvar.notify_all();
1582 }
1583 }
1584
1585 unsafe impl Send for EspHttpWsDetachedSender {}
1586
1587 impl Clone for EspHttpWsDetachedSender {
1588 fn clone(&self) -> Self {
1589 Self {
1590 sd: self.sd,
1591 fd: self.fd,
1592 closed: self.closed.clone(),
1593 }
1594 }
1595 }
1596
1597 impl ErrorType for EspHttpWsDetachedSender {
1598 type Error = EspError;
1599 }
1600
1601 impl Sender for EspHttpWsDetachedSender {
1602 fn send(&mut self, frame_type: FrameType, frame_data: &[u8]) -> Result<(), Self::Error> {
1603 EspHttpWsDetachedSender::send(self, frame_type, frame_data)
1604 }
1605 }
1606
1607 impl<'a> EspHttpServer<'a> {
1608 pub fn ws_handler<H, E>(
1624 &mut self,
1625 uri: &str,
1626 subprotocol_list: Option<&str>,
1627 handler: H,
1628 ) -> Result<&mut Self, EspError>
1629 where
1630 H: for<'r> Fn(&'r mut EspHttpWsConnection) -> Result<(), E> + Send + Sync + 'a,
1631 E: Debug,
1632 {
1633 let uri_c_str = to_cstring_arg(uri)?;
1634
1635 let (req_handler, close_handler) = self.to_native_ws_handler(self.sd, handler);
1636
1637 let mut conf = httpd_uri_t {
1638 uri: uri_c_str.as_ptr() as _,
1639 method: Newtype::<ffi::c_uint>::from(Method::Get).0,
1640 user_ctx: Box::into_raw(Box::new(req_handler)) as *mut _,
1641 handler: Some(EspHttpServer::handle_req),
1642 is_websocket: true,
1643 ..Default::default()
1645 };
1646
1647 let subproto_c_str; if let Some(subprotocol_list) = subprotocol_list {
1649 subproto_c_str = to_cstring_arg(subprotocol_list)?;
1650 conf.supported_subprotocol = subproto_c_str.as_ptr();
1651 }
1652
1653 esp!(unsafe { crate::sys::httpd_register_uri_handler(self.sd, &conf) })?;
1654
1655 {
1656 let mut all_close_handlers = CLOSE_HANDLERS.lock();
1657
1658 let close_handlers = all_close_handlers.get_mut(&(self.sd as u32)).unwrap();
1659
1660 let close_handler: CloseHandler<'static> =
1661 unsafe { core::mem::transmute(close_handler) };
1662
1663 close_handlers.push(close_handler);
1664 }
1665
1666 info!(
1667 "Registered Httpd server WS handler for URI \"{}\"",
1668 uri_c_str.to_str().unwrap()
1669 );
1670
1671 self.registrations.push((uri_c_str, conf));
1672
1673 Ok(self)
1674 }
1675
1676 fn handle_ws_request<H, E>(
1677 connection: &mut EspHttpWsConnection,
1678 handler: &H,
1679 ) -> Result<(), E>
1680 where
1681 H: for<'b> Fn(&'b mut EspHttpWsConnection) -> Result<(), E> + Send + 'a,
1682 E: Debug,
1683 {
1684 handler(connection)?;
1685
1686 Ok(())
1687 }
1688
1689 fn handle_ws_error<E>(error: E) -> ffi::c_int
1690 where
1691 E: Debug,
1692 {
1693 warn!("Unhandled internal error [{error:?}]:\n{error:?}");
1694
1695 ESP_OK as _
1696 }
1697
1698 fn to_native_ws_handler<H, E>(
1699 &self,
1700 server_handle: httpd_handle_t,
1701 handler: H,
1702 ) -> (NativeHandler<'a>, CloseHandler<'a>)
1703 where
1704 H: for<'r> Fn(&'r mut EspHttpWsConnection) -> Result<(), E> + Send + Sync + 'a,
1705 E: Debug,
1706 {
1707 let boxed_handler = Arc::new(move |mut connection: EspHttpWsConnection| {
1708 let result = Self::handle_ws_request(&mut connection, &handler);
1709
1710 match result {
1711 Ok(()) => ESP_OK as _,
1712 Err(e) => Self::handle_ws_error(e),
1713 }
1714 });
1715
1716 let req_handler = {
1717 let boxed_handler = boxed_handler.clone();
1718
1719 Box::new(move |raw_req: *mut httpd_req_t| {
1720 let req = unsafe { raw_req.as_ref() }.unwrap();
1721
1722 (boxed_handler)(if req.method == http_method_HTTP_GET as i32 {
1723 EspHttpWsConnection::New(server_handle, raw_req)
1724 } else {
1725 EspHttpWsConnection::Receiving(server_handle, raw_req, None)
1726 })
1727 })
1728 };
1729
1730 let close_handler = Box::new(move |fd| {
1731 (boxed_handler)(EspHttpWsConnection::Closed(fd));
1732 });
1733
1734 (req_handler, close_handler)
1735 }
1736 }
1737
1738 }