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 if !http11 => {
649 warn!("Chunked body with an HTTP/1.0 connection. This is not allowed.");
650 Err(HeadersMismatchError::BodyTypeError(
651 "Chunked body with an HTTP/1.0 connection. This is not allowed.",
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 base64::Engine;
935
936 use crate::Method;
937
938 pub const NONCE_LEN: usize = 16;
939 pub const MAX_BASE64_KEY_LEN: usize = 28;
940 pub const MAX_BASE64_KEY_RESPONSE_LEN: usize = 33;
941
942 pub const UPGRADE_REQUEST_HEADERS_LEN: usize = 7;
943 pub const UPGRADE_RESPONSE_HEADERS_LEN: usize = 4;
944
945 pub fn upgrade_request_headers<'a>(
954 host: Option<&'a str>,
955 origin: Option<&'a str>,
956 version: Option<&'a str>,
957 nonce: &[u8; NONCE_LEN],
958 buf: &'a mut [u8; MAX_BASE64_KEY_LEN],
959 ) -> [(&'a str, &'a str); UPGRADE_REQUEST_HEADERS_LEN] {
960 let host = host.map(|host| ("Host", host)).unwrap_or(("", ""));
961 let origin = origin.map(|origin| ("Origin", origin)).unwrap_or(("", ""));
962
963 [
964 host,
965 origin,
966 ("Content-Length", "0"),
967 ("Connection", "Upgrade"),
968 ("Upgrade", "websocket"),
969 ("Sec-WebSocket-Version", version.unwrap_or("13")),
970 ("Sec-WebSocket-Key", sec_key_encode(nonce, buf)),
971 ]
972 }
973
974 pub fn is_upgrade_request<'a, H>(method: Method, request_headers: H) -> bool
976 where
977 H: IntoIterator<Item = (&'a str, &'a str)>,
978 {
979 if method != Method::Get {
980 return false;
981 }
982
983 let mut connection = false;
984 let mut upgrade = false;
985
986 for (name, value) in request_headers {
987 if name.eq_ignore_ascii_case("Connection") {
988 connection = value.eq_ignore_ascii_case("Upgrade");
989 } else if name.eq_ignore_ascii_case("Upgrade") {
990 upgrade = value.eq_ignore_ascii_case("websocket");
991 }
992 }
993
994 connection && upgrade
995 }
996
997 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
999 pub enum UpgradeError {
1000 NoVersion,
1002 NoSecKey,
1004 UnsupportedVersion,
1006 }
1007
1008 impl core::fmt::Display for UpgradeError {
1009 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1010 match self {
1011 Self::NoVersion => write!(f, "No Sec-WebSocket-Version header"),
1012 Self::NoSecKey => write!(f, "No Sec-WebSocket-Key header"),
1013 Self::UnsupportedVersion => write!(f, "Unsupported Sec-WebSocket-Version"),
1014 }
1015 }
1016 }
1017
1018 #[cfg(feature = "defmt")]
1019 impl defmt::Format for UpgradeError {
1020 fn format(&self, f: defmt::Formatter<'_>) {
1021 match self {
1022 Self::NoVersion => defmt::write!(f, "No Sec-WebSocket-Version header"),
1023 Self::NoSecKey => defmt::write!(f, "No Sec-WebSocket-Key header"),
1024 Self::UnsupportedVersion => defmt::write!(f, "Unsupported Sec-WebSocket-Version"),
1025 }
1026 }
1027 }
1028
1029 impl core::error::Error for UpgradeError {}
1030
1031 pub fn upgrade_response_headers<'a, 'b, H>(
1038 request_headers: H,
1039 version: Option<&'a str>,
1040 buf: &'b mut [u8; MAX_BASE64_KEY_RESPONSE_LEN],
1041 ) -> Result<[(&'b str, &'b str); UPGRADE_RESPONSE_HEADERS_LEN], UpgradeError>
1042 where
1043 H: IntoIterator<Item = (&'a str, &'a str)>,
1044 {
1045 let mut version_ok = false;
1046 let mut sec_key_resp_len = None;
1047
1048 for (name, value) in request_headers {
1049 if name.eq_ignore_ascii_case("Sec-WebSocket-Version") {
1050 if !value.eq_ignore_ascii_case(version.unwrap_or("13")) {
1051 return Err(UpgradeError::NoVersion);
1052 }
1053
1054 version_ok = true;
1055 } else if name.eq_ignore_ascii_case("Sec-WebSocket-Key") {
1056 sec_key_resp_len = Some(sec_key_response(value, buf).len());
1057 }
1058 }
1059
1060 if version_ok {
1061 if let Some(sec_key_resp_len) = sec_key_resp_len {
1062 Ok([
1063 ("Content-Length", "0"),
1064 ("Connection", "Upgrade"),
1065 ("Upgrade", "websocket"),
1066 (
1067 "Sec-WebSocket-Accept",
1068 unwrap!(core::str::from_utf8(&buf[..sec_key_resp_len]).map_err(|_| ())),
1069 ),
1070 ])
1071 } else {
1072 Err(UpgradeError::NoSecKey)
1073 }
1074 } else {
1075 Err(UpgradeError::NoVersion)
1076 }
1077 }
1078
1079 pub fn is_upgrade_accepted<'a, H>(
1087 code: u16,
1088 response_headers: H,
1089 nonce: &[u8; NONCE_LEN],
1090 buf: &'a mut [u8; MAX_BASE64_KEY_RESPONSE_LEN],
1091 ) -> bool
1092 where
1093 H: IntoIterator<Item = (&'a str, &'a str)>,
1094 {
1095 if code != 101 {
1096 return false;
1097 }
1098
1099 let mut connection = false;
1100 let mut upgrade = false;
1101 let mut sec_key_response = false;
1102
1103 for (name, value) in response_headers {
1104 if name.eq_ignore_ascii_case("Connection") {
1105 connection = value.eq_ignore_ascii_case("Upgrade");
1106 } else if name.eq_ignore_ascii_case("Upgrade") {
1107 upgrade = value.eq_ignore_ascii_case("websocket");
1108 } else if name.eq_ignore_ascii_case("Sec-WebSocket-Accept") {
1109 let sec_key = sec_key_encode(nonce, buf);
1110
1111 let mut sha1 = sha1_smol::Sha1::new();
1112 sha1.update(sec_key.as_bytes());
1113
1114 let sec_key_resp = sec_key_response_finalize(&mut sha1, buf);
1115
1116 sec_key_response = value.eq(sec_key_resp);
1117 }
1118 }
1119
1120 connection && upgrade && sec_key_response
1121 }
1122
1123 fn sec_key_encode<'a>(nonce: &[u8], buf: &'a mut [u8]) -> &'a str {
1124 let nonce_base64_len = unwrap!(base64::engine::general_purpose::STANDARD
1125 .encode_slice(nonce, buf)
1126 .map_err(|_| ()));
1127
1128 unwrap!(core::str::from_utf8(&buf[..nonce_base64_len]).map_err(|_| ()))
1129 }
1130
1131 pub fn sec_key_response<'a>(
1133 sec_key: &str,
1134 buf: &'a mut [u8; MAX_BASE64_KEY_RESPONSE_LEN],
1135 ) -> &'a str {
1136 let mut sha1 = sha1_smol::Sha1::new();
1137
1138 sec_key_response_start(sec_key, &mut sha1);
1139 sec_key_response_finalize(&mut sha1, buf)
1140 }
1141
1142 fn sec_key_response_start(sec_key: &str, sha1: &mut sha1_smol::Sha1) {
1143 debug!("Computing response for key: {}", sec_key);
1144
1145 sha1.update(sec_key.as_bytes());
1146 }
1147
1148 fn sec_key_response_finalize<'a>(sha1: &mut sha1_smol::Sha1, buf: &'a mut [u8]) -> &'a str {
1149 const WS_MAGIC_GUUID: &str = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
1150
1151 sha1.update(WS_MAGIC_GUUID.as_bytes());
1152
1153 let len = unwrap!(base64::engine::general_purpose::STANDARD
1154 .encode_slice(sha1.digest().bytes(), buf)
1155 .map_err(|_| ()));
1156
1157 let sec_key_response = unwrap!(core::str::from_utf8(&buf[..len]).map_err(|_| ()));
1158
1159 debug!("Computed response: {}", sec_key_response);
1160
1161 sec_key_response
1162 }
1163}
1164
1165#[cfg(test)]
1166mod test {
1167 use crate::{
1168 ws::{sec_key_response, MAX_BASE64_KEY_RESPONSE_LEN},
1169 BodyType, ConnectionType,
1170 };
1171
1172 #[test]
1173 fn test_resp() {
1174 let mut buf = [0_u8; MAX_BASE64_KEY_RESPONSE_LEN];
1175 let resp = sec_key_response("dGhlIHNhbXBsZSBub25jZQ==", &mut buf);
1176
1177 assert_eq!(resp, "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=");
1178 }
1179
1180 #[test]
1181 fn test_resolve_conn() {
1182 assert_eq!(
1184 unwrap!(ConnectionType::resolve(None, None, true)),
1185 ConnectionType::KeepAlive
1186 );
1187 assert_eq!(
1188 unwrap!(ConnectionType::resolve(None, None, false)),
1189 ConnectionType::Close
1190 );
1191
1192 assert_eq!(
1194 unwrap!(ConnectionType::resolve(
1195 None,
1196 Some(ConnectionType::KeepAlive),
1197 false
1198 )),
1199 ConnectionType::KeepAlive
1200 );
1201 assert_eq!(
1202 unwrap!(ConnectionType::resolve(
1203 None,
1204 Some(ConnectionType::KeepAlive),
1205 true
1206 )),
1207 ConnectionType::KeepAlive
1208 );
1209
1210 assert_eq!(
1212 unwrap!(ConnectionType::resolve(
1213 Some(ConnectionType::Close),
1214 None,
1215 false
1216 )),
1217 ConnectionType::Close
1218 );
1219 assert_eq!(
1220 unwrap!(ConnectionType::resolve(
1221 Some(ConnectionType::KeepAlive),
1222 None,
1223 false
1224 )),
1225 ConnectionType::KeepAlive
1226 );
1227 assert_eq!(
1228 unwrap!(ConnectionType::resolve(
1229 Some(ConnectionType::Close),
1230 None,
1231 true
1232 )),
1233 ConnectionType::Close
1234 );
1235 assert_eq!(
1236 unwrap!(ConnectionType::resolve(
1237 Some(ConnectionType::KeepAlive),
1238 None,
1239 true
1240 )),
1241 ConnectionType::KeepAlive
1242 );
1243
1244 assert_eq!(
1246 unwrap!(ConnectionType::resolve(
1247 Some(ConnectionType::Close),
1248 Some(ConnectionType::Close),
1249 false
1250 )),
1251 ConnectionType::Close
1252 );
1253 assert_eq!(
1254 unwrap!(ConnectionType::resolve(
1255 Some(ConnectionType::KeepAlive),
1256 Some(ConnectionType::KeepAlive),
1257 false
1258 )),
1259 ConnectionType::KeepAlive
1260 );
1261 assert_eq!(
1262 unwrap!(ConnectionType::resolve(
1263 Some(ConnectionType::Close),
1264 Some(ConnectionType::Close),
1265 true
1266 )),
1267 ConnectionType::Close
1268 );
1269 assert_eq!(
1270 unwrap!(ConnectionType::resolve(
1271 Some(ConnectionType::KeepAlive),
1272 Some(ConnectionType::KeepAlive),
1273 true
1274 )),
1275 ConnectionType::KeepAlive
1276 );
1277 assert_eq!(
1278 unwrap!(ConnectionType::resolve(
1279 Some(ConnectionType::Close),
1280 Some(ConnectionType::KeepAlive),
1281 false
1282 )),
1283 ConnectionType::Close
1284 );
1285 assert!(ConnectionType::resolve(
1286 Some(ConnectionType::KeepAlive),
1287 Some(ConnectionType::Close),
1288 false
1289 )
1290 .is_err());
1291 assert_eq!(
1292 unwrap!(ConnectionType::resolve(
1293 Some(ConnectionType::Close),
1294 Some(ConnectionType::KeepAlive),
1295 true
1296 )),
1297 ConnectionType::Close
1298 );
1299 assert!(ConnectionType::resolve(
1300 Some(ConnectionType::KeepAlive),
1301 Some(ConnectionType::Close),
1302 true
1303 )
1304 .is_err());
1305 }
1306
1307 #[test]
1308 fn test_resolve_body() {
1309 assert_eq!(
1311 unwrap!(BodyType::resolve(
1312 None,
1313 ConnectionType::KeepAlive,
1314 true,
1315 true,
1316 false
1317 )),
1318 BodyType::ContentLen(0)
1319 );
1320 assert_eq!(
1321 unwrap!(BodyType::resolve(
1322 None,
1323 ConnectionType::Close,
1324 true,
1325 true,
1326 false
1327 )),
1328 BodyType::ContentLen(0)
1329 );
1330 assert_eq!(
1331 unwrap!(BodyType::resolve(
1332 None,
1333 ConnectionType::KeepAlive,
1334 true,
1335 false,
1336 false
1337 )),
1338 BodyType::ContentLen(0)
1339 );
1340 assert_eq!(
1341 unwrap!(BodyType::resolve(
1342 None,
1343 ConnectionType::Close,
1344 true,
1345 false,
1346 false
1347 )),
1348 BodyType::ContentLen(0)
1349 );
1350 assert_eq!(
1351 unwrap!(BodyType::resolve(
1352 None,
1353 ConnectionType::Upgrade,
1354 false,
1355 true,
1356 false
1357 )),
1358 BodyType::ContentLen(0)
1359 );
1360
1361 assert!(BodyType::resolve(None, ConnectionType::Upgrade, false, false, false).is_err());
1364
1365 assert!(BodyType::resolve(
1367 Some(BodyType::Chunked),
1368 ConnectionType::Close,
1369 true,
1370 false,
1371 false
1372 )
1373 .is_err());
1374 assert!(BodyType::resolve(
1375 Some(BodyType::Chunked),
1376 ConnectionType::KeepAlive,
1377 true,
1378 false,
1379 false
1380 )
1381 .is_err());
1382 assert!(BodyType::resolve(
1383 Some(BodyType::Chunked),
1384 ConnectionType::Close,
1385 false,
1386 false,
1387 false
1388 )
1389 .is_err());
1390 assert!(BodyType::resolve(
1391 Some(BodyType::Chunked),
1392 ConnectionType::KeepAlive,
1393 false,
1394 false,
1395 false
1396 )
1397 .is_err());
1398
1399 assert!(BodyType::resolve(
1401 Some(BodyType::Raw),
1402 ConnectionType::Close,
1403 true,
1404 true,
1405 false
1406 )
1407 .is_err());
1408 assert!(BodyType::resolve(
1409 Some(BodyType::Raw),
1410 ConnectionType::KeepAlive,
1411 true,
1412 true,
1413 false
1414 )
1415 .is_err());
1416 assert!(BodyType::resolve(
1417 Some(BodyType::Raw),
1418 ConnectionType::Close,
1419 true,
1420 false,
1421 false
1422 )
1423 .is_err());
1424 assert!(BodyType::resolve(
1425 Some(BodyType::Raw),
1426 ConnectionType::KeepAlive,
1427 true,
1428 false,
1429 false
1430 )
1431 .is_err());
1432
1433 assert!(BodyType::resolve(
1435 Some(BodyType::Raw),
1436 ConnectionType::KeepAlive,
1437 false,
1438 true,
1439 false
1440 )
1441 .is_err());
1442 assert!(BodyType::resolve(
1443 Some(BodyType::Raw),
1444 ConnectionType::KeepAlive,
1445 false,
1446 false,
1447 false
1448 )
1449 .is_err());
1450
1451 assert_eq!(
1453 unwrap!(BodyType::resolve(
1454 Some(BodyType::Raw),
1455 ConnectionType::Close,
1456 false,
1457 true,
1458 false
1459 )),
1460 BodyType::Raw
1461 );
1462 assert_eq!(
1463 unwrap!(BodyType::resolve(
1464 Some(BodyType::Raw),
1465 ConnectionType::Close,
1466 false,
1467 false,
1468 false
1469 )),
1470 BodyType::Raw
1471 );
1472
1473 assert_eq!(
1475 unwrap!(BodyType::resolve(
1476 None,
1477 ConnectionType::Close,
1478 true,
1479 true,
1480 true
1481 )),
1482 BodyType::Chunked
1483 );
1484 assert_eq!(
1485 unwrap!(BodyType::resolve(
1486 None,
1487 ConnectionType::KeepAlive,
1488 true,
1489 true,
1490 true
1491 )),
1492 BodyType::Chunked
1493 );
1494 assert_eq!(
1495 unwrap!(BodyType::resolve(
1496 None,
1497 ConnectionType::Close,
1498 true,
1499 false,
1500 true
1501 )),
1502 BodyType::ContentLen(0)
1503 );
1504 assert_eq!(
1505 unwrap!(BodyType::resolve(
1506 None,
1507 ConnectionType::KeepAlive,
1508 true,
1509 false,
1510 true
1511 )),
1512 BodyType::ContentLen(0)
1513 );
1514
1515 assert_eq!(
1517 unwrap!(BodyType::resolve(
1518 None,
1519 ConnectionType::KeepAlive,
1520 false,
1521 true,
1522 true
1523 )),
1524 BodyType::Chunked
1525 );
1526 assert_eq!(
1528 unwrap!(BodyType::resolve(
1529 None,
1530 ConnectionType::Close,
1531 false,
1532 true,
1533 true
1534 )),
1535 BodyType::Raw
1536 );
1537 }
1538}