1#![cfg_attr(not(feature = "std"), no_std)]
2#![allow(async_fn_in_trait)]
3#![warn(clippy::large_futures)]
4#![allow(clippy::uninlined_format_args)]
5#![allow(unknown_lints)]
6
7use core::fmt::Display;
8use core::str;
9
10use httparse::{Header, EMPTY_HEADER};
11
12use ws::{is_upgrade_accepted, is_upgrade_request, MAX_BASE64_KEY_RESPONSE_LEN, NONCE_LEN};
13
14pub const DEFAULT_MAX_HEADERS_COUNT: usize = 64;
15
16pub(crate) mod fmt;
18
19#[cfg(feature = "io")]
20pub mod io;
21
22#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
25pub enum HeadersMismatchError {
26 ResponseConnectionTypeMismatchError,
29 BodyTypeError(&'static str),
36}
37
38impl Display for HeadersMismatchError {
39 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
40 match self {
41 Self::ResponseConnectionTypeMismatchError => write!(
42 f,
43 "Response connection type is different from the request connection type"
44 ),
45 Self::BodyTypeError(s) => write!(f, "Body type mismatch: {s}"),
46 }
47 }
48}
49
50#[cfg(feature = "defmt")]
51impl defmt::Format for HeadersMismatchError {
52 fn format(&self, f: defmt::Formatter<'_>) {
53 match self {
54 Self::ResponseConnectionTypeMismatchError => defmt::write!(
55 f,
56 "Response connection type is different from the request connection type"
57 ),
58 Self::BodyTypeError(s) => defmt::write!(f, "Body type mismatch: {}", s),
59 }
60 }
61}
62
63#[derive(Copy, Clone, Debug, PartialEq, Eq)]
65#[cfg_attr(feature = "std", derive(Hash))]
66pub enum Method {
67 Delete,
68 Get,
69 Head,
70 Post,
71 Put,
72 Connect,
73 Options,
74 Trace,
75 Copy,
76 Lock,
77 MkCol,
78 Move,
79 Propfind,
80 Proppatch,
81 Search,
82 Unlock,
83 Bind,
84 Rebind,
85 Unbind,
86 Acl,
87 Report,
88 MkActivity,
89 Checkout,
90 Merge,
91 MSearch,
92 Notify,
93 Subscribe,
94 Unsubscribe,
95 Patch,
96 Purge,
97 MkCalendar,
98 Link,
99 Unlink,
100}
101
102impl Method {
103 pub fn new(method: &str) -> Option<Self> {
104 if method.eq_ignore_ascii_case("Delete") {
105 Some(Self::Delete)
106 } else if method.eq_ignore_ascii_case("Get") {
107 Some(Self::Get)
108 } else if method.eq_ignore_ascii_case("Head") {
109 Some(Self::Head)
110 } else if method.eq_ignore_ascii_case("Post") {
111 Some(Self::Post)
112 } else if method.eq_ignore_ascii_case("Put") {
113 Some(Self::Put)
114 } else if method.eq_ignore_ascii_case("Connect") {
115 Some(Self::Connect)
116 } else if method.eq_ignore_ascii_case("Options") {
117 Some(Self::Options)
118 } else if method.eq_ignore_ascii_case("Trace") {
119 Some(Self::Trace)
120 } else if method.eq_ignore_ascii_case("Copy") {
121 Some(Self::Copy)
122 } else if method.eq_ignore_ascii_case("Lock") {
123 Some(Self::Lock)
124 } else if method.eq_ignore_ascii_case("MkCol") {
125 Some(Self::MkCol)
126 } else if method.eq_ignore_ascii_case("Move") {
127 Some(Self::Move)
128 } else if method.eq_ignore_ascii_case("Propfind") {
129 Some(Self::Propfind)
130 } else if method.eq_ignore_ascii_case("Proppatch") {
131 Some(Self::Proppatch)
132 } else if method.eq_ignore_ascii_case("Search") {
133 Some(Self::Search)
134 } else if method.eq_ignore_ascii_case("Unlock") {
135 Some(Self::Unlock)
136 } else if method.eq_ignore_ascii_case("Bind") {
137 Some(Self::Bind)
138 } else if method.eq_ignore_ascii_case("Rebind") {
139 Some(Self::Rebind)
140 } else if method.eq_ignore_ascii_case("Unbind") {
141 Some(Self::Unbind)
142 } else if method.eq_ignore_ascii_case("Acl") {
143 Some(Self::Acl)
144 } else if method.eq_ignore_ascii_case("Report") {
145 Some(Self::Report)
146 } else if method.eq_ignore_ascii_case("MkActivity") {
147 Some(Self::MkActivity)
148 } else if method.eq_ignore_ascii_case("Checkout") {
149 Some(Self::Checkout)
150 } else if method.eq_ignore_ascii_case("Merge") {
151 Some(Self::Merge)
152 } else if method.eq_ignore_ascii_case("MSearch") {
153 Some(Self::MSearch)
154 } else if method.eq_ignore_ascii_case("Notify") {
155 Some(Self::Notify)
156 } else if method.eq_ignore_ascii_case("Subscribe") {
157 Some(Self::Subscribe)
158 } else if method.eq_ignore_ascii_case("Unsubscribe") {
159 Some(Self::Unsubscribe)
160 } else if method.eq_ignore_ascii_case("Patch") {
161 Some(Self::Patch)
162 } else if method.eq_ignore_ascii_case("Purge") {
163 Some(Self::Purge)
164 } else if method.eq_ignore_ascii_case("MkCalendar") {
165 Some(Self::MkCalendar)
166 } else if method.eq_ignore_ascii_case("Link") {
167 Some(Self::Link)
168 } else if method.eq_ignore_ascii_case("Unlink") {
169 Some(Self::Unlink)
170 } else {
171 None
172 }
173 }
174
175 fn as_str(&self) -> &'static str {
176 match self {
177 Self::Delete => "DELETE",
178 Self::Get => "GET",
179 Self::Head => "HEAD",
180 Self::Post => "POST",
181 Self::Put => "PUT",
182 Self::Connect => "CONNECT",
183 Self::Options => "OPTIONS",
184 Self::Trace => "TRACE",
185 Self::Copy => "COPY",
186 Self::Lock => "LOCK",
187 Self::MkCol => "MKCOL",
188 Self::Move => "MOVE",
189 Self::Propfind => "PROPFIND",
190 Self::Proppatch => "PROPPATCH",
191 Self::Search => "SEARCH",
192 Self::Unlock => "UNLOCK",
193 Self::Bind => "BIND",
194 Self::Rebind => "REBIND",
195 Self::Unbind => "UNBIND",
196 Self::Acl => "ACL",
197 Self::Report => "REPORT",
198 Self::MkActivity => "MKACTIVITY",
199 Self::Checkout => "CHECKOUT",
200 Self::Merge => "MERGE",
201 Self::MSearch => "MSEARCH",
202 Self::Notify => "NOTIFY",
203 Self::Subscribe => "SUBSCRIBE",
204 Self::Unsubscribe => "UNSUBSCRIBE",
205 Self::Patch => "PATCH",
206 Self::Purge => "PURGE",
207 Self::MkCalendar => "MKCALENDAR",
208 Self::Link => "LINK",
209 Self::Unlink => "UNLINK",
210 }
211 }
212}
213
214impl Display for Method {
215 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
216 write!(f, "{}", self.as_str())
217 }
218}
219
220#[cfg(feature = "defmt")]
221impl defmt::Format for Method {
222 fn format(&self, f: defmt::Formatter<'_>) {
223 defmt::write!(f, "{}", self.as_str())
224 }
225}
226
227#[derive(Debug)]
229pub struct Headers<'b, const N: usize = 64>([httparse::Header<'b>; N]);
230
231impl<'b, const N: usize> Headers<'b, N> {
232 #[inline(always)]
234 pub const fn new() -> Self {
235 Self([httparse::EMPTY_HEADER; N])
236 }
237
238 pub fn content_len(&self) -> Option<u64> {
240 self.get("Content-Length").map(|content_len_str| {
241 unwrap!(
242 content_len_str.parse::<u64>(),
243 "Invalid Content-Length header"
244 )
245 })
246 }
247
248 pub fn content_type(&self) -> Option<&str> {
250 self.get("Content-Type")
251 }
252
253 pub fn content_encoding(&self) -> Option<&str> {
255 self.get("Content-Encoding")
256 }
257
258 pub fn transfer_encoding(&self) -> Option<&str> {
260 self.get("Transfer-Encoding")
261 }
262
263 pub fn host(&self) -> Option<&str> {
265 self.get("Host")
266 }
267
268 pub fn connection(&self) -> Option<&str> {
270 self.get("Connection")
271 }
272
273 pub fn cache_control(&self) -> Option<&str> {
275 self.get("Cache-Control")
276 }
277
278 pub fn upgrade(&self) -> Option<&str> {
280 self.get("Upgrade")
281 }
282
283 pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
285 self.iter_raw()
286 .map(|(name, value)| (name, unsafe { str::from_utf8_unchecked(value) }))
287 }
288
289 pub fn iter_raw(&self) -> impl Iterator<Item = (&str, &[u8])> {
291 self.0
292 .iter()
293 .filter(|header| !header.name.is_empty())
294 .map(|header| (header.name, header.value))
295 }
296
297 pub fn get(&self, name: &str) -> Option<&str> {
299 self.iter()
300 .find(|(hname, _)| name.eq_ignore_ascii_case(hname))
301 .map(|(_, value)| value)
302 }
303
304 pub fn get_raw(&self, name: &str) -> Option<&[u8]> {
306 self.iter_raw()
307 .find(|(hname, _)| name.eq_ignore_ascii_case(hname))
308 .map(|(_, value)| value)
309 }
310
311 pub fn set(&mut self, name: &'b str, value: &'b str) -> &mut Self {
313 self.set_raw(name, value.as_bytes())
314 }
315
316 pub fn set_raw(&mut self, name: &'b str, value: &'b [u8]) -> &mut Self {
318 if !name.is_empty() {
319 for header in &mut self.0 {
320 if header.name.is_empty() || header.name.eq_ignore_ascii_case(name) {
321 *header = Header { name, value };
322 return self;
323 }
324 }
325
326 panic!("No space left");
327 } else {
328 self.remove(name)
329 }
330 }
331
332 pub fn remove(&mut self, name: &str) -> &mut Self {
334 let index = self
335 .0
336 .iter()
337 .enumerate()
338 .find(|(_, header)| header.name.eq_ignore_ascii_case(name));
339
340 if let Some((mut index, _)) = index {
341 while index < self.0.len() - 1 {
342 self.0[index] = self.0[index + 1];
343
344 index += 1;
345 }
346
347 self.0[index] = EMPTY_HEADER;
348 }
349
350 self
351 }
352
353 pub fn set_content_len(
355 &mut self,
356 content_len: u64,
357 buf: &'b mut heapless::String<20>,
358 ) -> &mut Self {
359 *buf = unwrap!(content_len.try_into());
360
361 self.set("Content-Length", buf.as_str())
362 }
363
364 pub fn set_content_type(&mut self, content_type: &'b str) -> &mut Self {
366 self.set("Content-Type", content_type)
367 }
368
369 pub fn set_content_encoding(&mut self, content_encoding: &'b str) -> &mut Self {
371 self.set("Content-Encoding", content_encoding)
372 }
373
374 pub fn set_transfer_encoding(&mut self, transfer_encoding: &'b str) -> &mut Self {
376 self.set("Transfer-Encoding", transfer_encoding)
377 }
378
379 pub fn set_transfer_encoding_chunked(&mut self) -> &mut Self {
381 self.set_transfer_encoding("Chunked")
382 }
383
384 pub fn set_host(&mut self, host: &'b str) -> &mut Self {
386 self.set("Host", host)
387 }
388
389 pub fn set_connection(&mut self, connection: &'b str) -> &mut Self {
391 self.set("Connection", connection)
392 }
393
394 pub fn set_connection_close(&mut self) -> &mut Self {
396 self.set_connection("Close")
397 }
398
399 pub fn set_connection_keep_alive(&mut self) -> &mut Self {
401 self.set_connection("Keep-Alive")
402 }
403
404 pub fn set_connection_upgrade(&mut self) -> &mut Self {
406 self.set_connection("Upgrade")
407 }
408
409 pub fn set_cache_control(&mut self, cache: &'b str) -> &mut Self {
411 self.set("Cache-Control", cache)
412 }
413
414 pub fn set_cache_control_no_cache(&mut self) -> &mut Self {
416 self.set_cache_control("No-Cache")
417 }
418
419 pub fn set_upgrade(&mut self, upgrade: &'b str) -> &mut Self {
421 self.set("Upgrade", upgrade)
422 }
423
424 pub fn set_upgrade_websocket(&mut self) -> &mut Self {
426 self.set_upgrade("websocket")
427 }
428
429 pub fn set_ws_upgrade_request_headers(
432 &mut self,
433 host: Option<&'b str>,
434 origin: Option<&'b str>,
435 version: Option<&'b str>,
436 nonce: &[u8; ws::NONCE_LEN],
437 buf: &'b mut [u8; ws::MAX_BASE64_KEY_LEN],
438 ) -> &mut Self {
439 for (name, value) in ws::upgrade_request_headers(host, origin, version, nonce, buf) {
440 self.set(name, value);
441 }
442
443 self
444 }
445
446 pub fn set_ws_upgrade_response_headers<'a, H>(
449 &mut self,
450 request_headers: H,
451 version: Option<&'a str>,
452 buf: &'b mut [u8; ws::MAX_BASE64_KEY_RESPONSE_LEN],
453 ) -> Result<&mut Self, ws::UpgradeError>
454 where
455 H: IntoIterator<Item = (&'a str, &'a str)>,
456 {
457 for (name, value) in ws::upgrade_response_headers(request_headers, version, buf)? {
458 self.set(name, value);
459 }
460
461 Ok(self)
462 }
463}
464
465impl<const N: usize> Default for Headers<'_, N> {
466 fn default() -> Self {
467 Self::new()
468 }
469}
470
471#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
473pub enum ConnectionType {
474 KeepAlive,
475 Close,
476 Upgrade,
477}
478
479impl ConnectionType {
480 pub fn resolve(
493 headers_connection_type: Option<ConnectionType>,
494 carry_over_connection_type: Option<ConnectionType>,
495 http11: bool,
496 ) -> Result<Self, HeadersMismatchError> {
497 match headers_connection_type {
498 Some(connection_type) => {
499 if let Some(carry_over_connection_type) = carry_over_connection_type {
500 if matches!(connection_type, ConnectionType::KeepAlive)
501 && matches!(carry_over_connection_type, ConnectionType::Close)
502 {
503 warn!("Cannot set a Keep-Alive connection when the peer requested Close");
504 Err(HeadersMismatchError::ResponseConnectionTypeMismatchError)?;
505 }
506 }
507
508 Ok(connection_type)
509 }
510 None => {
511 if let Some(carry_over_connection_type) = carry_over_connection_type {
512 Ok(carry_over_connection_type)
513 } else if http11 {
514 Ok(Self::KeepAlive)
515 } else {
516 Ok(Self::Close)
517 }
518 }
519 }
520 }
521
522 pub fn from_header(name: &str, value: &str) -> Option<Self> {
526 if "Connection".eq_ignore_ascii_case(name) && value.eq_ignore_ascii_case("Close") {
527 Some(Self::Close)
528 } else if "Connection".eq_ignore_ascii_case(name)
529 && value.eq_ignore_ascii_case("Keep-Alive")
530 {
531 Some(Self::KeepAlive)
532 } else if "Connection".eq_ignore_ascii_case(name) && value.eq_ignore_ascii_case("Upgrade") {
533 Some(Self::Upgrade)
534 } else {
535 None
536 }
537 }
538
539 pub fn from_headers<'a, H>(headers: H) -> Option<Self>
544 where
545 H: IntoIterator<Item = (&'a str, &'a str)>,
546 {
547 let mut connection = None;
548
549 for (name, value) in headers {
550 let header_connection = Self::from_header(name, value);
551
552 if let Some(header_connection) = header_connection {
553 if let Some(connection) = connection {
554 warn!(
555 "Multiple Connection headers found. Current {} and new {}",
556 connection, header_connection
557 );
558 }
559
560 connection = Some(header_connection);
562 }
563 }
564
565 connection
566 }
567
568 pub fn raw_header(&self) -> (&str, &[u8]) {
570 let connection = match self {
571 Self::KeepAlive => "Keep-Alive",
572 Self::Close => "Close",
573 Self::Upgrade => "Upgrade",
574 };
575
576 ("Connection", connection.as_bytes())
577 }
578}
579
580impl Display for ConnectionType {
581 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
582 match self {
583 Self::KeepAlive => write!(f, "Keep-Alive"),
584 Self::Close => write!(f, "Close"),
585 Self::Upgrade => write!(f, "Upgrade"),
586 }
587 }
588}
589
590#[cfg(feature = "defmt")]
591impl defmt::Format for ConnectionType {
592 fn format(&self, f: defmt::Formatter<'_>) {
593 match self {
594 Self::KeepAlive => defmt::write!(f, "Keep-Alive"),
595 Self::Close => defmt::write!(f, "Close"),
596 Self::Upgrade => defmt::write!(f, "Upgrade"),
597 }
598 }
599}
600
601#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
603pub enum BodyType {
604 Chunked,
606 ContentLen(u64),
608 Raw,
610}
611
612impl BodyType {
613 pub fn resolve(
626 headers_body_type: Option<BodyType>,
627 connection_type: ConnectionType,
628 request: bool,
629 http11: bool,
630 chunked_if_unspecified: bool,
631 ) -> Result<Self, HeadersMismatchError> {
632 match headers_body_type {
633 Some(headers_body_type) => {
634 match headers_body_type {
635 BodyType::Raw => {
636 if request {
637 warn!("Raw body in a request. This is not allowed.");
638 Err(HeadersMismatchError::BodyTypeError(
639 "Raw body in a request. This is not allowed.",
640 ))?;
641 } else if !matches!(connection_type, ConnectionType::Close) {
642 warn!("Raw body response with a Keep-Alive connection. This is not allowed.");
643 Err(HeadersMismatchError::BodyTypeError("Raw body response with a Keep-Alive connection. This is not allowed."))?;
644 }
645 }
646 BodyType::Chunked => {
647 if !http11 {
648 warn!("Chunked body with an HTTP/1.0 connection. This is not allowed.");
649 Err(HeadersMismatchError::BodyTypeError(
650 "Chunked body with an HTTP/1.0 connection. This is not allowed.",
651 ))?;
652 }
653 }
654 _ => {}
655 }
656
657 Ok(headers_body_type)
658 }
659 None => {
660 if request {
661 if chunked_if_unspecified && http11 {
662 Ok(BodyType::Chunked)
664 } else {
665 debug!("Unknown body type in a request. Assuming Content-Length=0.");
666 Ok(BodyType::ContentLen(0))
667 }
668 } else if matches!(connection_type, ConnectionType::Close) {
669 Ok(BodyType::Raw)
670 } else if matches!(connection_type, ConnectionType::Upgrade) {
671 if http11 {
672 debug!("Unknown body type in response but the Connection is Upgrade. Assuming Content-Length=0.");
673 Ok(BodyType::ContentLen(0))
674 } else {
675 warn!("Connection is set to Upgrade but the HTTP protocol version is not 1.1. This is not allowed.");
676 Err(HeadersMismatchError::BodyTypeError(
677 "Connection is set to Upgrade but the HTTP protocol version is not 1.1. This is not allowed.",
678 ))
679 }
680 } else if chunked_if_unspecified && http11 {
681 Ok(BodyType::Chunked)
683 } else {
684 warn!("Unknown body type in a response with a Keep-Alive connection. This is not allowed.");
685 Err(HeadersMismatchError::BodyTypeError("Unknown body type in a response with a Keep-Alive connection. This is not allowed."))
686 }
687 }
688 }
689 }
690
691 pub fn from_header(name: &str, value: &str) -> Option<Self> {
695 if "Transfer-Encoding".eq_ignore_ascii_case(name) {
696 if value.eq_ignore_ascii_case("Chunked") {
697 return Some(Self::Chunked);
698 }
699 } else if "Content-Length".eq_ignore_ascii_case(name) {
700 return Some(Self::ContentLen(unwrap!(
701 value.parse::<u64>(),
702 "Invalid Content-Length header"
703 ))); }
705
706 None
707 }
708
709 pub fn from_headers<'a, H>(headers: H) -> Option<Self>
714 where
715 H: IntoIterator<Item = (&'a str, &'a str)>,
716 {
717 let mut body = None;
718
719 for (name, value) in headers {
720 let header_body = Self::from_header(name, value);
721
722 if let Some(header_body) = header_body {
723 if let Some(body) = body {
724 warn!(
725 "Multiple body type headers found. Current {} and new {}",
726 body, header_body
727 );
728 }
729
730 body = Some(header_body);
732 }
733 }
734
735 body
736 }
737
738 pub fn raw_header<'a>(&self, buf: &'a mut heapless::String<20>) -> Option<(&str, &'a [u8])> {
744 match self {
745 Self::Chunked => Some(("Transfer-Encoding", "Chunked".as_bytes())),
746 Self::ContentLen(len) => {
747 use core::fmt::Write;
748
749 buf.clear();
750
751 write_unwrap!(buf, "{}", len);
752
753 Some(("Content-Length", buf.as_bytes()))
754 }
755 Self::Raw => None,
756 }
757 }
758}
759
760impl Display for BodyType {
761 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
762 match self {
763 Self::Chunked => write!(f, "Chunked"),
764 Self::ContentLen(len) => write!(f, "Content-Length: {len}"),
765 Self::Raw => write!(f, "Raw"),
766 }
767 }
768}
769
770#[cfg(feature = "defmt")]
771impl defmt::Format for BodyType {
772 fn format(&self, f: defmt::Formatter<'_>) {
773 match self {
774 Self::Chunked => defmt::write!(f, "Chunked"),
775 Self::ContentLen(len) => defmt::write!(f, "Content-Length: {}", len),
776 Self::Raw => defmt::write!(f, "Raw"),
777 }
778 }
779}
780
781#[derive(Debug)]
783pub struct RequestHeaders<'b, const N: usize> {
784 pub http11: bool,
786 pub method: Method,
788 pub path: &'b str,
790 pub headers: Headers<'b, N>,
792}
793
794impl<const N: usize> RequestHeaders<'_, N> {
795 #[inline(always)]
797 pub const fn new() -> Self {
798 Self {
799 http11: true,
800 method: Method::Get,
801 path: "/",
802 headers: Headers::new(),
803 }
804 }
805
806 pub fn is_ws_upgrade_request(&self) -> bool {
808 is_upgrade_request(self.method, self.headers.iter())
809 }
810}
811
812impl<const N: usize> Default for RequestHeaders<'_, N> {
813 #[inline(always)]
814 fn default() -> Self {
815 Self::new()
816 }
817}
818
819impl<const N: usize> Display for RequestHeaders<'_, N> {
820 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
821 write!(f, "{} ", if self.http11 { "HTTP/1.1" } else { "HTTP/1.0" })?;
822
823 writeln!(f, "{} {}", self.method, self.path)?;
824
825 for (name, value) in self.headers.iter() {
826 if name.is_empty() {
827 break;
828 }
829
830 writeln!(f, "{name}: {value}")?;
831 }
832
833 Ok(())
834 }
835}
836
837#[cfg(feature = "defmt")]
838impl<const N: usize> defmt::Format for RequestHeaders<'_, N> {
839 fn format(&self, f: defmt::Formatter<'_>) {
840 defmt::write!(f, "{} ", if self.http11 { "HTTP/1.1" } else { "HTTP/1.0" });
841
842 defmt::write!(f, "{} {}\n", self.method, self.path);
843
844 for (name, value) in self.headers.iter() {
845 if name.is_empty() {
846 break;
847 }
848
849 defmt::write!(f, "{}: {}\n", name, value);
850 }
851 }
852}
853
854#[derive(Debug)]
856pub struct ResponseHeaders<'b, const N: usize> {
857 pub http11: bool,
859 pub code: u16,
861 pub reason: Option<&'b str>,
863 pub headers: Headers<'b, N>,
865}
866
867impl<const N: usize> ResponseHeaders<'_, N> {
868 #[inline(always)]
870 pub const fn new() -> Self {
871 Self {
872 http11: true,
873 code: 200,
874 reason: None,
875 headers: Headers::new(),
876 }
877 }
878
879 pub fn is_ws_upgrade_accepted(
882 &self,
883 nonce: &[u8; NONCE_LEN],
884 buf: &mut [u8; MAX_BASE64_KEY_RESPONSE_LEN],
885 ) -> bool {
886 is_upgrade_accepted(self.code, self.headers.iter(), nonce, buf)
887 }
888}
889
890impl<const N: usize> Default for ResponseHeaders<'_, N> {
891 #[inline(always)]
892 fn default() -> Self {
893 Self::new()
894 }
895}
896
897impl<const N: usize> Display for ResponseHeaders<'_, N> {
898 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
899 write!(f, "{} ", if self.http11 { "HTTP/1.1 " } else { "HTTP/1.0" })?;
900
901 writeln!(f, "{} {}", self.code, self.reason.unwrap_or(""))?;
902
903 for (name, value) in self.headers.iter() {
904 if name.is_empty() {
905 break;
906 }
907
908 writeln!(f, "{name}: {value}")?;
909 }
910
911 Ok(())
912 }
913}
914
915#[cfg(feature = "defmt")]
916impl<const N: usize> defmt::Format for ResponseHeaders<'_, N> {
917 fn format(&self, f: defmt::Formatter<'_>) {
918 defmt::write!(f, "{} ", if self.http11 { "HTTP/1.1 " } else { "HTTP/1.0" });
919
920 defmt::write!(f, "{} {}\n", self.code, self.reason.unwrap_or(""));
921
922 for (name, value) in self.headers.iter() {
923 if name.is_empty() {
924 break;
925 }
926
927 defmt::write!(f, "{}: {}\n", name, value);
928 }
929 }
930}
931
932pub mod ws {
934 use crate::Method;
935
936 pub const NONCE_LEN: usize = 16;
937 pub const MAX_BASE64_KEY_LEN: usize = 28;
938 pub const MAX_BASE64_KEY_RESPONSE_LEN: usize = 33;
939
940 pub const UPGRADE_REQUEST_HEADERS_LEN: usize = 7;
941 pub const UPGRADE_RESPONSE_HEADERS_LEN: usize = 4;
942
943 pub fn upgrade_request_headers<'a>(
952 host: Option<&'a str>,
953 origin: Option<&'a str>,
954 version: Option<&'a str>,
955 nonce: &[u8; NONCE_LEN],
956 buf: &'a mut [u8; MAX_BASE64_KEY_LEN],
957 ) -> [(&'a str, &'a str); UPGRADE_REQUEST_HEADERS_LEN] {
958 let host = host.map(|host| ("Host", host)).unwrap_or(("", ""));
959 let origin = origin.map(|origin| ("Origin", origin)).unwrap_or(("", ""));
960
961 [
962 host,
963 origin,
964 ("Content-Length", "0"),
965 ("Connection", "Upgrade"),
966 ("Upgrade", "websocket"),
967 ("Sec-WebSocket-Version", version.unwrap_or("13")),
968 ("Sec-WebSocket-Key", sec_key_encode(nonce, buf)),
969 ]
970 }
971
972 pub fn is_upgrade_request<'a, H>(method: Method, request_headers: H) -> bool
974 where
975 H: IntoIterator<Item = (&'a str, &'a str)>,
976 {
977 if method != Method::Get {
978 return false;
979 }
980
981 let mut connection = false;
982 let mut upgrade = false;
983
984 for (name, value) in request_headers {
985 if name.eq_ignore_ascii_case("Connection") {
986 connection = value.eq_ignore_ascii_case("Upgrade");
987 } else if name.eq_ignore_ascii_case("Upgrade") {
988 upgrade = value.eq_ignore_ascii_case("websocket");
989 }
990 }
991
992 connection && upgrade
993 }
994
995 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
997 pub enum UpgradeError {
998 NoVersion,
1000 NoSecKey,
1002 UnsupportedVersion,
1004 }
1005
1006 impl core::fmt::Display for UpgradeError {
1007 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1008 match self {
1009 Self::NoVersion => write!(f, "No Sec-WebSocket-Version header"),
1010 Self::NoSecKey => write!(f, "No Sec-WebSocket-Key header"),
1011 Self::UnsupportedVersion => write!(f, "Unsupported Sec-WebSocket-Version"),
1012 }
1013 }
1014 }
1015
1016 #[cfg(feature = "defmt")]
1017 impl defmt::Format for UpgradeError {
1018 fn format(&self, f: defmt::Formatter<'_>) {
1019 match self {
1020 Self::NoVersion => defmt::write!(f, "No Sec-WebSocket-Version header"),
1021 Self::NoSecKey => defmt::write!(f, "No Sec-WebSocket-Key header"),
1022 Self::UnsupportedVersion => defmt::write!(f, "Unsupported Sec-WebSocket-Version"),
1023 }
1024 }
1025 }
1026
1027 #[cfg(feature = "std")]
1028 impl std::error::Error for UpgradeError {}
1029
1030 pub fn upgrade_response_headers<'a, 'b, H>(
1037 request_headers: H,
1038 version: Option<&'a str>,
1039 buf: &'b mut [u8; MAX_BASE64_KEY_RESPONSE_LEN],
1040 ) -> Result<[(&'b str, &'b str); UPGRADE_RESPONSE_HEADERS_LEN], UpgradeError>
1041 where
1042 H: IntoIterator<Item = (&'a str, &'a str)>,
1043 {
1044 let mut version_ok = false;
1045 let mut sec_key_resp_len = None;
1046
1047 for (name, value) in request_headers {
1048 if name.eq_ignore_ascii_case("Sec-WebSocket-Version") {
1049 if !value.eq_ignore_ascii_case(version.unwrap_or("13")) {
1050 return Err(UpgradeError::NoVersion);
1051 }
1052
1053 version_ok = true;
1054 } else if name.eq_ignore_ascii_case("Sec-WebSocket-Key") {
1055 sec_key_resp_len = Some(sec_key_response(value, buf).len());
1056 }
1057 }
1058
1059 if version_ok {
1060 if let Some(sec_key_resp_len) = sec_key_resp_len {
1061 Ok([
1062 ("Content-Length", "0"),
1063 ("Connection", "Upgrade"),
1064 ("Upgrade", "websocket"),
1065 ("Sec-WebSocket-Accept", unsafe {
1066 core::str::from_utf8_unchecked(&buf[..sec_key_resp_len])
1067 }),
1068 ])
1069 } else {
1070 Err(UpgradeError::NoSecKey)
1071 }
1072 } else {
1073 Err(UpgradeError::NoVersion)
1074 }
1075 }
1076
1077 pub fn is_upgrade_accepted<'a, H>(
1085 code: u16,
1086 response_headers: H,
1087 nonce: &[u8; NONCE_LEN],
1088 buf: &'a mut [u8; MAX_BASE64_KEY_RESPONSE_LEN],
1089 ) -> bool
1090 where
1091 H: IntoIterator<Item = (&'a str, &'a str)>,
1092 {
1093 if code != 101 {
1094 return false;
1095 }
1096
1097 let mut connection = false;
1098 let mut upgrade = false;
1099 let mut sec_key_response = false;
1100
1101 for (name, value) in response_headers {
1102 if name.eq_ignore_ascii_case("Connection") {
1103 connection = value.eq_ignore_ascii_case("Upgrade");
1104 } else if name.eq_ignore_ascii_case("Upgrade") {
1105 upgrade = value.eq_ignore_ascii_case("websocket");
1106 } else if name.eq_ignore_ascii_case("Sec-WebSocket-Accept") {
1107 let sec_key = sec_key_encode(nonce, buf);
1108
1109 let mut sha1 = sha1_smol::Sha1::new();
1110 sha1.update(sec_key.as_bytes());
1111
1112 let sec_key_resp = sec_key_response_finalize(&mut sha1, buf);
1113
1114 sec_key_response = value.eq(sec_key_resp);
1115 }
1116 }
1117
1118 connection && upgrade && sec_key_response
1119 }
1120
1121 fn sec_key_encode<'a>(nonce: &[u8], buf: &'a mut [u8]) -> &'a str {
1122 let nonce_base64_len = base64::encode_config_slice(nonce, base64::STANDARD, buf);
1123
1124 unsafe { core::str::from_utf8_unchecked(&buf[..nonce_base64_len]) }
1125 }
1126
1127 pub fn sec_key_response<'a>(
1129 sec_key: &str,
1130 buf: &'a mut [u8; MAX_BASE64_KEY_RESPONSE_LEN],
1131 ) -> &'a str {
1132 let mut sha1 = sha1_smol::Sha1::new();
1133
1134 sec_key_response_start(sec_key, &mut sha1);
1135 sec_key_response_finalize(&mut sha1, buf)
1136 }
1137
1138 fn sec_key_response_start(sec_key: &str, sha1: &mut sha1_smol::Sha1) {
1139 debug!("Computing response for key: {}", sec_key);
1140
1141 sha1.update(sec_key.as_bytes());
1142 }
1143
1144 fn sec_key_response_finalize<'a>(sha1: &mut sha1_smol::Sha1, buf: &'a mut [u8]) -> &'a str {
1145 const WS_MAGIC_GUUID: &str = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
1146
1147 sha1.update(WS_MAGIC_GUUID.as_bytes());
1148
1149 let len = base64::encode_config_slice(sha1.digest().bytes(), base64::STANDARD, buf);
1150
1151 let sec_key_response = unsafe { core::str::from_utf8_unchecked(&buf[..len]) };
1152
1153 debug!("Computed response: {}", sec_key_response);
1154
1155 sec_key_response
1156 }
1157}
1158
1159#[cfg(test)]
1160mod test {
1161 use crate::{
1162 ws::{sec_key_response, MAX_BASE64_KEY_RESPONSE_LEN},
1163 BodyType, ConnectionType,
1164 };
1165
1166 #[test]
1167 fn test_resp() {
1168 let mut buf = [0_u8; MAX_BASE64_KEY_RESPONSE_LEN];
1169 let resp = sec_key_response("dGhlIHNhbXBsZSBub25jZQ==", &mut buf);
1170
1171 assert_eq!(resp, "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=");
1172 }
1173
1174 #[test]
1175 fn test_resolve_conn() {
1176 assert_eq!(
1178 unwrap!(ConnectionType::resolve(None, None, true)),
1179 ConnectionType::KeepAlive
1180 );
1181 assert_eq!(
1182 unwrap!(ConnectionType::resolve(None, None, false)),
1183 ConnectionType::Close
1184 );
1185
1186 assert_eq!(
1188 unwrap!(ConnectionType::resolve(
1189 None,
1190 Some(ConnectionType::KeepAlive),
1191 false
1192 )),
1193 ConnectionType::KeepAlive
1194 );
1195 assert_eq!(
1196 unwrap!(ConnectionType::resolve(
1197 None,
1198 Some(ConnectionType::KeepAlive),
1199 true
1200 )),
1201 ConnectionType::KeepAlive
1202 );
1203
1204 assert_eq!(
1206 unwrap!(ConnectionType::resolve(
1207 Some(ConnectionType::Close),
1208 None,
1209 false
1210 )),
1211 ConnectionType::Close
1212 );
1213 assert_eq!(
1214 unwrap!(ConnectionType::resolve(
1215 Some(ConnectionType::KeepAlive),
1216 None,
1217 false
1218 )),
1219 ConnectionType::KeepAlive
1220 );
1221 assert_eq!(
1222 unwrap!(ConnectionType::resolve(
1223 Some(ConnectionType::Close),
1224 None,
1225 true
1226 )),
1227 ConnectionType::Close
1228 );
1229 assert_eq!(
1230 unwrap!(ConnectionType::resolve(
1231 Some(ConnectionType::KeepAlive),
1232 None,
1233 true
1234 )),
1235 ConnectionType::KeepAlive
1236 );
1237
1238 assert_eq!(
1240 unwrap!(ConnectionType::resolve(
1241 Some(ConnectionType::Close),
1242 Some(ConnectionType::Close),
1243 false
1244 )),
1245 ConnectionType::Close
1246 );
1247 assert_eq!(
1248 unwrap!(ConnectionType::resolve(
1249 Some(ConnectionType::KeepAlive),
1250 Some(ConnectionType::KeepAlive),
1251 false
1252 )),
1253 ConnectionType::KeepAlive
1254 );
1255 assert_eq!(
1256 unwrap!(ConnectionType::resolve(
1257 Some(ConnectionType::Close),
1258 Some(ConnectionType::Close),
1259 true
1260 )),
1261 ConnectionType::Close
1262 );
1263 assert_eq!(
1264 unwrap!(ConnectionType::resolve(
1265 Some(ConnectionType::KeepAlive),
1266 Some(ConnectionType::KeepAlive),
1267 true
1268 )),
1269 ConnectionType::KeepAlive
1270 );
1271 assert_eq!(
1272 unwrap!(ConnectionType::resolve(
1273 Some(ConnectionType::Close),
1274 Some(ConnectionType::KeepAlive),
1275 false
1276 )),
1277 ConnectionType::Close
1278 );
1279 assert!(ConnectionType::resolve(
1280 Some(ConnectionType::KeepAlive),
1281 Some(ConnectionType::Close),
1282 false
1283 )
1284 .is_err());
1285 assert_eq!(
1286 unwrap!(ConnectionType::resolve(
1287 Some(ConnectionType::Close),
1288 Some(ConnectionType::KeepAlive),
1289 true
1290 )),
1291 ConnectionType::Close
1292 );
1293 assert!(ConnectionType::resolve(
1294 Some(ConnectionType::KeepAlive),
1295 Some(ConnectionType::Close),
1296 true
1297 )
1298 .is_err());
1299 }
1300
1301 #[test]
1302 fn test_resolve_body() {
1303 assert_eq!(
1305 unwrap!(BodyType::resolve(
1306 None,
1307 ConnectionType::KeepAlive,
1308 true,
1309 true,
1310 false
1311 )),
1312 BodyType::ContentLen(0)
1313 );
1314 assert_eq!(
1315 unwrap!(BodyType::resolve(
1316 None,
1317 ConnectionType::Close,
1318 true,
1319 true,
1320 false
1321 )),
1322 BodyType::ContentLen(0)
1323 );
1324 assert_eq!(
1325 unwrap!(BodyType::resolve(
1326 None,
1327 ConnectionType::KeepAlive,
1328 true,
1329 false,
1330 false
1331 )),
1332 BodyType::ContentLen(0)
1333 );
1334 assert_eq!(
1335 unwrap!(BodyType::resolve(
1336 None,
1337 ConnectionType::Close,
1338 true,
1339 false,
1340 false
1341 )),
1342 BodyType::ContentLen(0)
1343 );
1344 assert_eq!(
1345 unwrap!(BodyType::resolve(
1346 None,
1347 ConnectionType::Upgrade,
1348 false,
1349 true,
1350 false
1351 )),
1352 BodyType::ContentLen(0)
1353 );
1354
1355 assert!(BodyType::resolve(None, ConnectionType::Upgrade, false, false, false).is_err());
1358
1359 assert!(BodyType::resolve(
1361 Some(BodyType::Chunked),
1362 ConnectionType::Close,
1363 true,
1364 false,
1365 false
1366 )
1367 .is_err());
1368 assert!(BodyType::resolve(
1369 Some(BodyType::Chunked),
1370 ConnectionType::KeepAlive,
1371 true,
1372 false,
1373 false
1374 )
1375 .is_err());
1376 assert!(BodyType::resolve(
1377 Some(BodyType::Chunked),
1378 ConnectionType::Close,
1379 false,
1380 false,
1381 false
1382 )
1383 .is_err());
1384 assert!(BodyType::resolve(
1385 Some(BodyType::Chunked),
1386 ConnectionType::KeepAlive,
1387 false,
1388 false,
1389 false
1390 )
1391 .is_err());
1392
1393 assert!(BodyType::resolve(
1395 Some(BodyType::Raw),
1396 ConnectionType::Close,
1397 true,
1398 true,
1399 false
1400 )
1401 .is_err());
1402 assert!(BodyType::resolve(
1403 Some(BodyType::Raw),
1404 ConnectionType::KeepAlive,
1405 true,
1406 true,
1407 false
1408 )
1409 .is_err());
1410 assert!(BodyType::resolve(
1411 Some(BodyType::Raw),
1412 ConnectionType::Close,
1413 true,
1414 false,
1415 false
1416 )
1417 .is_err());
1418 assert!(BodyType::resolve(
1419 Some(BodyType::Raw),
1420 ConnectionType::KeepAlive,
1421 true,
1422 false,
1423 false
1424 )
1425 .is_err());
1426
1427 assert!(BodyType::resolve(
1429 Some(BodyType::Raw),
1430 ConnectionType::KeepAlive,
1431 false,
1432 true,
1433 false
1434 )
1435 .is_err());
1436 assert!(BodyType::resolve(
1437 Some(BodyType::Raw),
1438 ConnectionType::KeepAlive,
1439 false,
1440 false,
1441 false
1442 )
1443 .is_err());
1444
1445 assert_eq!(
1447 unwrap!(BodyType::resolve(
1448 Some(BodyType::Raw),
1449 ConnectionType::Close,
1450 false,
1451 true,
1452 false
1453 )),
1454 BodyType::Raw
1455 );
1456 assert_eq!(
1457 unwrap!(BodyType::resolve(
1458 Some(BodyType::Raw),
1459 ConnectionType::Close,
1460 false,
1461 false,
1462 false
1463 )),
1464 BodyType::Raw
1465 );
1466
1467 assert_eq!(
1469 unwrap!(BodyType::resolve(
1470 None,
1471 ConnectionType::Close,
1472 true,
1473 true,
1474 true
1475 )),
1476 BodyType::Chunked
1477 );
1478 assert_eq!(
1479 unwrap!(BodyType::resolve(
1480 None,
1481 ConnectionType::KeepAlive,
1482 true,
1483 true,
1484 true
1485 )),
1486 BodyType::Chunked
1487 );
1488 assert_eq!(
1489 unwrap!(BodyType::resolve(
1490 None,
1491 ConnectionType::Close,
1492 true,
1493 false,
1494 true
1495 )),
1496 BodyType::ContentLen(0)
1497 );
1498 assert_eq!(
1499 unwrap!(BodyType::resolve(
1500 None,
1501 ConnectionType::KeepAlive,
1502 true,
1503 false,
1504 true
1505 )),
1506 BodyType::ContentLen(0)
1507 );
1508
1509 assert_eq!(
1511 unwrap!(BodyType::resolve(
1512 None,
1513 ConnectionType::KeepAlive,
1514 false,
1515 true,
1516 true
1517 )),
1518 BodyType::Chunked
1519 );
1520 assert_eq!(
1522 unwrap!(BodyType::resolve(
1523 None,
1524 ConnectionType::Close,
1525 false,
1526 true,
1527 true
1528 )),
1529 BodyType::Raw
1530 );
1531 }
1532}
1533
1534#[cfg(feature = "embedded-svc")]
1535mod embedded_svc_compat {
1536 use core::str;
1537
1538 use embedded_svc::http::client::asynch::Method;
1539
1540 impl From<Method> for super::Method {
1541 fn from(method: Method) -> Self {
1542 match method {
1543 Method::Delete => super::Method::Delete,
1544 Method::Get => super::Method::Get,
1545 Method::Head => super::Method::Head,
1546 Method::Post => super::Method::Post,
1547 Method::Put => super::Method::Put,
1548 Method::Connect => super::Method::Connect,
1549 Method::Options => super::Method::Options,
1550 Method::Trace => super::Method::Trace,
1551 Method::Copy => super::Method::Copy,
1552 Method::Lock => super::Method::Lock,
1553 Method::MkCol => super::Method::MkCol,
1554 Method::Move => super::Method::Move,
1555 Method::Propfind => super::Method::Propfind,
1556 Method::Proppatch => super::Method::Proppatch,
1557 Method::Search => super::Method::Search,
1558 Method::Unlock => super::Method::Unlock,
1559 Method::Bind => super::Method::Bind,
1560 Method::Rebind => super::Method::Rebind,
1561 Method::Unbind => super::Method::Unbind,
1562 Method::Acl => super::Method::Acl,
1563 Method::Report => super::Method::Report,
1564 Method::MkActivity => super::Method::MkActivity,
1565 Method::Checkout => super::Method::Checkout,
1566 Method::Merge => super::Method::Merge,
1567 Method::MSearch => super::Method::MSearch,
1568 Method::Notify => super::Method::Notify,
1569 Method::Subscribe => super::Method::Subscribe,
1570 Method::Unsubscribe => super::Method::Unsubscribe,
1571 Method::Patch => super::Method::Patch,
1572 Method::Purge => super::Method::Purge,
1573 Method::MkCalendar => super::Method::MkCalendar,
1574 Method::Link => super::Method::Link,
1575 Method::Unlink => super::Method::Unlink,
1576 }
1577 }
1578 }
1579
1580 impl From<super::Method> for Method {
1581 fn from(method: super::Method) -> Self {
1582 match method {
1583 super::Method::Delete => Method::Delete,
1584 super::Method::Get => Method::Get,
1585 super::Method::Head => Method::Head,
1586 super::Method::Post => Method::Post,
1587 super::Method::Put => Method::Put,
1588 super::Method::Connect => Method::Connect,
1589 super::Method::Options => Method::Options,
1590 super::Method::Trace => Method::Trace,
1591 super::Method::Copy => Method::Copy,
1592 super::Method::Lock => Method::Lock,
1593 super::Method::MkCol => Method::MkCol,
1594 super::Method::Move => Method::Move,
1595 super::Method::Propfind => Method::Propfind,
1596 super::Method::Proppatch => Method::Proppatch,
1597 super::Method::Search => Method::Search,
1598 super::Method::Unlock => Method::Unlock,
1599 super::Method::Bind => Method::Bind,
1600 super::Method::Rebind => Method::Rebind,
1601 super::Method::Unbind => Method::Unbind,
1602 super::Method::Acl => Method::Acl,
1603 super::Method::Report => Method::Report,
1604 super::Method::MkActivity => Method::MkActivity,
1605 super::Method::Checkout => Method::Checkout,
1606 super::Method::Merge => Method::Merge,
1607 super::Method::MSearch => Method::MSearch,
1608 super::Method::Notify => Method::Notify,
1609 super::Method::Subscribe => Method::Subscribe,
1610 super::Method::Unsubscribe => Method::Unsubscribe,
1611 super::Method::Patch => Method::Patch,
1612 super::Method::Purge => Method::Purge,
1613 super::Method::MkCalendar => Method::MkCalendar,
1614 super::Method::Link => Method::Link,
1615 super::Method::Unlink => Method::Unlink,
1616 }
1617 }
1618 }
1619
1620 impl<const N: usize> embedded_svc::http::Query for super::RequestHeaders<'_, N> {
1621 fn uri(&self) -> &'_ str {
1622 self.path
1623 }
1624
1625 fn method(&self) -> Method {
1626 self.method.into()
1627 }
1628 }
1629
1630 impl<const N: usize> embedded_svc::http::Headers for super::RequestHeaders<'_, N> {
1631 fn header(&self, name: &str) -> Option<&'_ str> {
1632 self.headers.get(name)
1633 }
1634 }
1635
1636 impl<const N: usize> embedded_svc::http::Status for super::ResponseHeaders<'_, N> {
1637 fn status(&self) -> u16 {
1638 self.code
1639 }
1640
1641 fn status_message(&self) -> Option<&'_ str> {
1642 self.reason
1643 }
1644 }
1645
1646 impl<const N: usize> embedded_svc::http::Headers for super::ResponseHeaders<'_, N> {
1647 fn header(&self, name: &str) -> Option<&'_ str> {
1648 self.headers.get(name)
1649 }
1650 }
1651
1652 impl<const N: usize> embedded_svc::http::Headers for super::Headers<'_, N> {
1653 fn header(&self, name: &str) -> Option<&'_ str> {
1654 self.get(name)
1655 }
1656 }
1657}