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