1use crate::error::WebServerError;
4use bytes::Bytes;
5use http::{HeaderMap, Method, StatusCode as HttpStatusCode, Uri, Version};
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::path::PathBuf;
9use std::time::{Duration, SystemTime};
10
11#[derive(Debug)]
13pub struct Request {
14 pub method: HttpMethod,
15 pub uri: Uri,
16 pub version: Version,
17 pub headers: Headers,
18 pub body: Body,
19 pub extensions: HashMap<String, String>, pub path_params: HashMap<String, String>,
22 pub cookies: HashMap<String, Cookie>,
24 pub form_data: Option<HashMap<String, String>>,
26 pub multipart: Option<MultipartForm>,
28}
29
30impl Request {
31 pub fn new(method: HttpMethod, uri: Uri) -> Self {
33 Self {
34 method,
35 uri,
36 version: Version::HTTP_11,
37 headers: Headers::new(),
38 body: Body::empty(),
39 extensions: HashMap::new(),
40 path_params: HashMap::new(),
41 cookies: HashMap::new(),
42 form_data: None,
43 multipart: None,
44 }
45 }
46
47 pub fn path(&self) -> &str {
49 self.uri.path()
50 }
51
52 pub fn query(&self) -> Option<&str> {
54 self.uri.query()
55 }
56
57 pub fn param(&self, name: &str) -> Option<&str> {
59 self.path_params.get(name).map(|s| s.as_str())
60 }
61
62 pub fn params(&self) -> &HashMap<String, String> {
64 &self.path_params
65 }
66
67 pub fn set_params(&mut self, params: HashMap<String, String>) {
69 self.path_params = params;
70 }
71
72 pub async fn json<T>(&self) -> crate::error::Result<T>
74 where
75 T: for<'de> Deserialize<'de>,
76 {
77 let bytes = self.body.bytes().await?;
78 serde_json::from_slice(&bytes).map_err(crate::error::WebServerError::JsonError)
79 }
80
81 pub async fn text(&self) -> crate::error::Result<String> {
83 let bytes = self.body.bytes().await?;
84 String::from_utf8(bytes.to_vec()).map_err(crate::error::WebServerError::Utf8Error)
85 }
86
87 pub fn cookie(&self, name: &str) -> Option<&Cookie> {
89 self.cookies.get(name)
90 }
91
92 pub fn cookies(&self) -> &HashMap<String, Cookie> {
94 &self.cookies
95 }
96
97 pub fn parse_cookies(&mut self) -> crate::error::Result<()> {
99 if let Some(cookie_header) = self.headers.get("Cookie") {
100 let cookies = Cookie::parse(cookie_header)?;
101 for cookie in cookies {
102 self.cookies.insert(cookie.name.clone(), cookie);
103 }
104 }
105 Ok(())
106 }
107
108 pub fn form(&self, name: &str) -> Option<&str> {
110 self.form_data.as_ref()?.get(name).map(|s| s.as_str())
111 }
112
113 pub fn form_data(&self) -> Option<&HashMap<String, String>> {
115 self.form_data.as_ref()
116 }
117
118 pub async fn parse_form(&mut self) -> crate::error::Result<()> {
120 if self.form_data.is_some() {
121 return Ok(()); }
123
124 let default_content_type = String::new();
125 let content_type = self
126 .headers
127 .get("Content-Type")
128 .unwrap_or(&default_content_type);
129 if !content_type.starts_with("application/x-www-form-urlencoded") {
130 return Ok(()); }
132
133 let body_text = self.text().await?;
134 let mut form_data = HashMap::new();
135
136 for pair in body_text.split('&') {
137 if let Some((key, value)) = pair.split_once('=') {
138 let key = key.replace("%20", " ").replace("+", " ");
140 let value = value.replace("%20", " ").replace("+", " ");
141 form_data.insert(key, value);
142 }
143 }
144
145 self.form_data = Some(form_data);
146 Ok(())
147 }
148
149 pub fn multipart(&self) -> Option<&MultipartForm> {
151 self.multipart.as_ref()
152 }
153
154 pub fn query_params(&self) -> HashMap<String, String> {
156 if let Some(query) = self.uri.query() {
157 let mut params = HashMap::new();
158 for pair in query.split('&') {
159 if let Some((key, value)) = pair.split_once('=') {
160 let key = urlencoding::decode(key).unwrap_or_default().into_owned();
162 let value = urlencoding::decode(value).unwrap_or_default().into_owned();
163 params.insert(key, value);
164 }
165 }
166 params
167 } else {
168 HashMap::new()
169 }
170 }
171
172 pub fn query_param(&self, name: &str) -> Option<String> {
174 self.query_params().get(name).cloned()
175 }
176
177 pub fn accepts(&self, content_type: &str) -> bool {
179 if let Some(accept_header) = self.headers.get("Accept") {
180 accept_header.contains(content_type) || accept_header.contains("*/*")
181 } else {
182 true }
184 }
185
186 pub fn content_type(&self) -> Option<&str> {
188 self.headers.get("Content-Type").map(|s| s.as_str())
189 }
190
191 pub fn is_json(&self) -> bool {
193 self.content_type()
194 .is_some_and(|ct| ct.contains("application/json"))
195 }
196
197 pub fn is_form(&self) -> bool {
199 self.content_type()
200 .is_some_and(|ct| ct.contains("application/x-www-form-urlencoded"))
201 }
202
203 pub fn is_multipart(&self) -> bool {
205 self.content_type()
206 .is_some_and(|ct| ct.contains("multipart/form-data"))
207 }
208
209 pub fn remote_addr(&self) -> Option<&str> {
211 if let Some(forwarded) = self.headers.get("X-Forwarded-For") {
213 if let Some(first_ip) = forwarded.split(',').next() {
215 return Some(first_ip.trim());
216 }
217 }
218
219 if let Some(real_ip) = self.headers.get("X-Real-IP") {
221 return Some(real_ip.as_str());
222 }
223
224 self.extensions.get("remote_addr").map(|s| s.as_str())
226 }
227
228 pub fn user_agent(&self) -> Option<&str> {
230 self.headers.get("User-Agent").map(|s| s.as_str())
231 }
232
233 pub fn path_param(&self, name: &str) -> Option<&str> {
235 self.path_params.get(name).map(|s| s.as_str())
236 }
237
238 pub fn path_params(&self) -> &HashMap<String, String> {
240 &self.path_params
241 }
242
243 pub fn set_path_param(&mut self, name: impl Into<String>, value: impl Into<String>) {
245 self.path_params.insert(name.into(), value.into());
246 }
247
248 pub async fn parse_multipart(&mut self) -> crate::error::Result<()> {
250 if self.multipart.is_some() {
251 return Ok(()); }
253
254 let default_content_type = String::new();
255 let content_type = self
256 .headers
257 .get("Content-Type")
258 .unwrap_or(&default_content_type);
259 if !content_type.starts_with("multipart/form-data") {
260 return Ok(()); }
262
263 let boundary = match content_type.split(";").nth(1) {
265 Some(part) => {
266 let part = part.trim();
267 if part.starts_with("boundary=") {
268 part.trim_start_matches("boundary=").trim_matches('"')
269 } else {
270 return Err(WebServerError::parse_error(
271 "Missing boundary in multipart Content-Type",
272 ));
273 }
274 }
275 None => {
276 return Err(WebServerError::parse_error(
277 "Missing boundary in multipart Content-Type",
278 ));
279 }
280 };
281
282 let mut form = MultipartForm::new();
284
285 let body_bytes = self.body.bytes().await?;
287
288 let mut current_part: Option<MultipartPart> = None;
290 let mut parsing_headers = true;
291 let mut part_content: Vec<u8> = Vec::new();
292
293 let boundary_start = format!("--{}", boundary);
294 let boundary_end = format!("--{}--", boundary);
295
296 let body_str = String::from_utf8_lossy(&body_bytes);
298 let lines: Vec<&str> = body_str.split("\r\n").collect();
299
300 let mut i = 0;
301 while i < lines.len() {
302 let line = lines[i];
303
304 if line == boundary_start {
306 if let Some(part) = current_part.take() {
308 form.add_part(part);
309 }
310
311 current_part = Some(MultipartPart::new());
313 parsing_headers = true;
314 part_content = Vec::new();
315 } else if line == boundary_end {
316 if let Some(part) = current_part.take() {
318 form.add_part(part);
319 }
320 break;
321 } else if parsing_headers {
322 if line.is_empty() {
324 parsing_headers = false;
326 } else if let Some(part) = &mut current_part {
327 if let Some((name, value)) = line.split_once(":") {
329 let name = name.trim();
330 let value = value.trim();
331
332 if name.eq_ignore_ascii_case("Content-Disposition") {
334 for param in value.split(";") {
336 let param = param.trim();
337
338 if param.starts_with("name=") {
339 let field_name =
340 param.trim_start_matches("name=").trim_matches('"');
341 part.field_name = Some(field_name.to_string());
342 } else if param.starts_with("filename=") {
343 let filename =
344 param.trim_start_matches("filename=").trim_matches('"');
345 part.filename = Some(filename.to_string());
346 }
347 }
348 }
349
350 part.headers.insert(name.to_string(), value.to_string());
351 }
352 }
353 } else {
354 if let Some(_part) = &mut current_part {
356 part_content.extend_from_slice(line.as_bytes());
357 if i < lines.len() - 1 && !lines[i + 1].starts_with("--") {
359 part_content.extend_from_slice(b"\r\n");
360 }
361 }
362 }
363
364 i += 1;
365 }
366
367 self.multipart = Some(form);
369
370 Ok(())
371 }
372}
373
374#[derive(Debug, Clone)]
376pub struct Response {
377 pub status: StatusCode,
378 pub headers: Headers,
379 pub body: Body,
380}
381
382impl Response {
383 pub fn new(status: StatusCode) -> Self {
385 Self {
386 status,
387 headers: Headers::new(),
388 body: Body::empty(),
389 }
390 }
391
392 pub fn ok() -> Self {
394 Self::new(StatusCode::OK)
395 }
396
397 pub fn body<B>(mut self, body: B) -> Self
399 where
400 B: Into<Body>,
401 {
402 self.body = body.into();
403 self
404 }
405
406 pub fn header<K, V>(mut self, key: K, value: V) -> Self
408 where
409 K: Into<String>,
410 V: Into<String>,
411 {
412 self.headers.insert(key.into(), value.into());
413 self
414 }
415
416 pub fn json<T>(value: &T) -> crate::error::Result<Self>
418 where
419 T: Serialize,
420 {
421 let json = serde_json::to_string(value)?;
422 Ok(Self::ok()
423 .header("content-type", "application/json")
424 .body(json))
425 }
426
427 pub fn cookie(mut self, cookie: Cookie) -> Self {
429 self.headers.set("Set-Cookie", cookie.to_header_value());
430 self
431 }
432
433 pub fn cookies(mut self, cookies: Vec<Cookie>) -> Self {
435 for cookie in cookies {
436 self.headers.add("Set-Cookie", cookie.to_header_value());
437 }
438 self
439 }
440
441 pub fn html(content: impl Into<String>) -> Self {
443 Self::ok()
444 .header("Content-Type", "text/html; charset=utf-8")
445 .body(content.into())
446 }
447
448 pub fn text(content: impl Into<String>) -> Self {
450 Self::ok()
451 .header("Content-Type", "text/plain; charset=utf-8")
452 .body(content.into())
453 }
454
455 pub fn redirect(location: impl Into<String>) -> Self {
457 Self::new(StatusCode::FOUND).header("Location", location.into())
458 }
459
460 pub fn redirect_permanent(location: impl Into<String>) -> Self {
462 Self::new(StatusCode::MOVED_PERMANENTLY).header("Location", location.into())
463 }
464
465 pub fn not_found() -> Self {
467 Self::new(StatusCode::NOT_FOUND).body("Not Found")
468 }
469
470 pub fn bad_request(message: impl Into<String>) -> Self {
472 Self::new(StatusCode::BAD_REQUEST).body(message.into())
473 }
474
475 pub fn internal_server_error(message: impl Into<String>) -> Self {
477 Self::new(StatusCode::INTERNAL_SERVER_ERROR).body(message.into())
478 }
479
480 pub fn cache(mut self, max_age: u32) -> Self {
482 self.headers
483 .insert("Cache-Control".to_string(), format!("max-age={}", max_age));
484 self
485 }
486
487 pub fn no_cache(mut self) -> Self {
489 self.headers.insert(
490 "Cache-Control".to_string(),
491 "no-cache, no-store, must-revalidate".to_string(),
492 );
493 self.headers
494 .insert("Pragma".to_string(), "no-cache".to_string());
495 self.headers.insert("Expires".to_string(), "0".to_string());
496 self
497 }
498
499 pub fn cors(mut self) -> Self {
501 self.headers
502 .insert("Access-Control-Allow-Origin".to_string(), "*".to_string());
503 self.headers.insert(
504 "Access-Control-Allow-Methods".to_string(),
505 "GET, POST, PUT, DELETE, OPTIONS".to_string(),
506 );
507 self.headers.insert(
508 "Access-Control-Allow-Headers".to_string(),
509 "Content-Type, Authorization".to_string(),
510 );
511 self
512 }
513
514 pub fn cors_origin(mut self, origin: impl Into<String>) -> Self {
516 self.headers
517 .insert("Access-Control-Allow-Origin".to_string(), origin.into());
518 self.headers.insert(
519 "Access-Control-Allow-Methods".to_string(),
520 "GET, POST, PUT, DELETE, OPTIONS".to_string(),
521 );
522 self.headers.insert(
523 "Access-Control-Allow-Headers".to_string(),
524 "Content-Type, Authorization".to_string(),
525 );
526 self
527 }
528
529 pub async fn file(path: impl Into<PathBuf>) -> crate::error::Result<Self> {
531 let path = path.into();
532
533 let data = std::fs::read(&path).map_err(|e| {
535 if e.kind() == std::io::ErrorKind::NotFound {
536 return crate::error::WebServerError::custom("File not found");
537 }
538 crate::error::WebServerError::custom(format!("Failed to read file: {}", e))
539 })?;
540
541 let content_type = mime_guess::from_path(&path)
543 .first_or_octet_stream()
544 .to_string();
545
546 Ok(Self::ok().header("Content-Type", content_type).body(data))
547 }
548
549 pub fn download(filename: impl Into<String>, data: impl Into<Vec<u8>>) -> Self {
551 let filename = filename.into();
552 Self::ok()
553 .header("Content-Type", "application/octet-stream")
554 .header(
555 "Content-Disposition",
556 format!("attachment; filename=\"{}\"", filename),
557 )
558 .body(data.into())
559 }
560
561 pub fn with_content_length(mut self) -> Self {
563 let length = self.body.len();
564 self.headers
565 .insert("Content-Length".to_string(), length.to_string());
566 self
567 }
568}
569
570#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
572pub enum HttpMethod {
573 GET,
574 POST,
575 PUT,
576 DELETE,
577 PATCH,
578 HEAD,
579 OPTIONS,
580 TRACE,
581 CONNECT,
582}
583
584impl HttpMethod {
585 pub fn as_str(&self) -> &'static str {
586 match self {
587 HttpMethod::GET => "GET",
588 HttpMethod::POST => "POST",
589 HttpMethod::PUT => "PUT",
590 HttpMethod::DELETE => "DELETE",
591 HttpMethod::PATCH => "PATCH",
592 HttpMethod::HEAD => "HEAD",
593 HttpMethod::OPTIONS => "OPTIONS",
594 HttpMethod::TRACE => "TRACE",
595 HttpMethod::CONNECT => "CONNECT",
596 }
597 }
598}
599
600impl From<Method> for HttpMethod {
601 fn from(method: Method) -> Self {
602 match method {
603 Method::GET => Self::GET,
604 Method::POST => Self::POST,
605 Method::PUT => Self::PUT,
606 Method::DELETE => Self::DELETE,
607 Method::PATCH => Self::PATCH,
608 Method::HEAD => Self::HEAD,
609 Method::OPTIONS => Self::OPTIONS,
610 Method::TRACE => Self::TRACE,
611 Method::CONNECT => Self::CONNECT,
612 _ => Self::GET, }
614 }
615}
616
617impl From<HttpMethod> for Method {
618 fn from(method: HttpMethod) -> Self {
619 match method {
620 HttpMethod::GET => Method::GET,
621 HttpMethod::POST => Method::POST,
622 HttpMethod::PUT => Method::PUT,
623 HttpMethod::DELETE => Method::DELETE,
624 HttpMethod::PATCH => Method::PATCH,
625 HttpMethod::HEAD => Method::HEAD,
626 HttpMethod::OPTIONS => Method::OPTIONS,
627 HttpMethod::TRACE => Method::TRACE,
628 HttpMethod::CONNECT => Method::CONNECT,
629 }
630 }
631}
632
633#[derive(Debug, Clone, Copy, PartialEq, Eq)]
635pub struct StatusCode(pub u16);
636
637impl StatusCode {
638 pub const OK: StatusCode = StatusCode(200);
639 pub const CREATED: StatusCode = StatusCode(201);
640 pub const NO_CONTENT: StatusCode = StatusCode(204);
641 pub const MOVED_PERMANENTLY: StatusCode = StatusCode(301);
642 pub const FOUND: StatusCode = StatusCode(302);
643 pub const BAD_REQUEST: StatusCode = StatusCode(400);
644 pub const UNAUTHORIZED: StatusCode = StatusCode(401);
645 pub const FORBIDDEN: StatusCode = StatusCode(403);
646 pub const NOT_FOUND: StatusCode = StatusCode(404);
647 pub const METHOD_NOT_ALLOWED: StatusCode = StatusCode(405);
648 pub const INTERNAL_SERVER_ERROR: StatusCode = StatusCode(500);
649 pub const BAD_GATEWAY: StatusCode = StatusCode(502);
650 pub const SERVICE_UNAVAILABLE: StatusCode = StatusCode(503);
651 pub const SWITCHING_PROTOCOLS: StatusCode = StatusCode(101);
652
653 pub fn as_u16(&self) -> u16 {
654 self.0
655 }
656}
657
658impl From<HttpStatusCode> for StatusCode {
659 fn from(status: HttpStatusCode) -> Self {
660 StatusCode(status.as_u16())
661 }
662}
663
664impl From<StatusCode> for HttpStatusCode {
665 fn from(status: StatusCode) -> Self {
666 HttpStatusCode::from_u16(status.0).unwrap_or(HttpStatusCode::INTERNAL_SERVER_ERROR)
667 }
668}
669
670#[derive(Debug, Clone)]
672pub struct Headers {
673 inner: HashMap<String, String>,
674}
675
676impl Default for Headers {
677 fn default() -> Self {
678 Self::new()
679 }
680}
681
682impl Headers {
683 pub fn new() -> Self {
684 Self {
685 inner: HashMap::new(),
686 }
687 }
688
689 pub fn insert(&mut self, key: String, value: String) {
690 self.inner.insert(key.to_lowercase(), value);
691 }
692
693 pub fn get(&self, key: &str) -> Option<&String> {
694 self.inner.get(&key.to_lowercase())
695 }
696
697 pub fn iter(&self) -> impl Iterator<Item = (&String, &String)> {
698 self.inner.iter()
699 }
700
701 pub fn set(&mut self, key: &str, value: String) {
703 self.insert(key.to_string(), value);
704 }
705
706 pub fn add(&mut self, key: &str, value: String) {
708 let key_lower = key.to_lowercase();
709 if let Some(_existing) = self.inner.get(&key_lower) {
710 self.inner.insert(key_lower, value);
713 } else {
714 self.inner.insert(key_lower, value);
715 }
716 }
717}
718
719impl From<HeaderMap> for Headers {
720 fn from(headers: HeaderMap) -> Self {
721 let mut inner = HashMap::new();
722 for (key, value) in headers.iter() {
723 if let Ok(value_str) = value.to_str() {
724 inner.insert(key.to_string(), value_str.to_string());
725 }
726 }
727 Self { inner }
728 }
729}
730
731impl From<Headers> for HeaderMap {
732 fn from(headers: Headers) -> Self {
733 let mut header_map = HeaderMap::new();
734 for (key, value) in headers.inner {
735 if let (Ok(name), Ok(val)) = (
736 key.parse::<http::HeaderName>(),
737 value.parse::<http::HeaderValue>(),
738 ) {
739 header_map.insert(name, val);
740 }
741 }
742 header_map
743 }
744}
745
746#[derive(Debug, Clone)]
748pub struct Body {
749 data: Bytes,
750}
751
752impl Body {
753 pub fn empty() -> Self {
754 Self { data: Bytes::new() }
755 }
756
757 pub fn from_bytes(bytes: Bytes) -> Self {
758 Self { data: bytes }
759 }
760
761 pub async fn bytes(&self) -> crate::error::Result<Bytes> {
762 Ok(self.data.clone())
763 }
764
765 pub fn len(&self) -> usize {
766 self.data.len()
767 }
768
769 pub fn is_empty(&self) -> bool {
770 self.data.is_empty()
771 }
772}
773
774impl From<String> for Body {
775 fn from(s: String) -> Self {
776 Self::from_bytes(Bytes::from(s))
777 }
778}
779
780impl From<&str> for Body {
781 fn from(s: &str) -> Self {
782 Self::from_bytes(Bytes::from(s.to_string()))
783 }
784}
785
786impl From<Vec<u8>> for Body {
787 fn from(data: Vec<u8>) -> Self {
788 Self::from_bytes(Bytes::from(data))
789 }
790}
791
792impl From<Bytes> for Body {
793 fn from(bytes: Bytes) -> Self {
794 Self::from_bytes(bytes)
795 }
796}
797
798#[derive(Debug, Clone, PartialEq)]
800pub struct Cookie {
801 pub name: String,
802 pub value: String,
803 pub domain: Option<String>,
804 pub path: Option<String>,
805 pub expires: Option<SystemTime>,
806 pub max_age: Option<Duration>,
807 pub secure: bool,
808 pub http_only: bool,
809 pub same_site: Option<SameSite>,
810}
811
812#[derive(Debug, Clone, PartialEq)]
813pub enum SameSite {
814 Strict,
815 Lax,
816 None,
817}
818
819impl Cookie {
820 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
821 Self {
822 name: name.into(),
823 value: value.into(),
824 domain: None,
825 path: None,
826 expires: None,
827 max_age: None,
828 secure: false,
829 http_only: false,
830 same_site: None,
831 }
832 }
833
834 pub fn domain(mut self, domain: impl Into<String>) -> Self {
835 self.domain = Some(domain.into());
836 self
837 }
838
839 pub fn path(mut self, path: impl Into<String>) -> Self {
840 self.path = Some(path.into());
841 self
842 }
843
844 pub fn expires(mut self, expires: SystemTime) -> Self {
845 self.expires = Some(expires);
846 self
847 }
848
849 pub fn max_age(mut self, max_age: Duration) -> Self {
850 self.max_age = Some(max_age);
851 self
852 }
853
854 pub fn secure(mut self, secure: bool) -> Self {
855 self.secure = secure;
856 self
857 }
858
859 pub fn http_only(mut self, http_only: bool) -> Self {
860 self.http_only = http_only;
861 self
862 }
863
864 pub fn same_site(mut self, same_site: SameSite) -> Self {
865 self.same_site = Some(same_site);
866 self
867 }
868
869 pub fn parse(header_value: &str) -> crate::error::Result<Vec<Cookie>> {
871 let mut cookies = Vec::new();
872
873 for cookie_str in header_value.split(';') {
874 let cookie_str = cookie_str.trim();
875 if let Some((name, value)) = cookie_str.split_once('=') {
876 cookies.push(Cookie::new(name.trim(), value.trim()));
877 }
878 }
879
880 Ok(cookies)
881 }
882
883 pub fn to_header_value(&self) -> String {
885 let mut result = format!("{}={}", self.name, self.value);
886
887 if let Some(ref domain) = self.domain {
888 result.push_str(&format!("; Domain={}", domain));
889 }
890
891 if let Some(ref path) = self.path {
892 result.push_str(&format!("; Path={}", path));
893 }
894
895 if let Some(expires) = self.expires
896 && let Ok(duration) = expires.duration_since(SystemTime::UNIX_EPOCH)
897 {
898 result.push_str(&format!("; Expires={}", duration.as_secs()));
899 }
900
901 if let Some(max_age) = self.max_age {
902 result.push_str(&format!("; Max-Age={}", max_age.as_secs()));
903 }
904
905 if self.secure {
906 result.push_str("; Secure");
907 }
908
909 if self.http_only {
910 result.push_str("; HttpOnly");
911 }
912
913 if let Some(ref same_site) = self.same_site {
914 let same_site_str = match same_site {
915 SameSite::Strict => "Strict",
916 SameSite::Lax => "Lax",
917 SameSite::None => "None",
918 };
919 result.push_str(&format!("; SameSite={}", same_site_str));
920 }
921
922 result
923 }
924}
925
926#[derive(Debug, Clone)]
928pub enum FormValue {
929 Text(String),
930 Binary(Vec<u8>),
931 File(FileUpload),
932}
933
934#[derive(Debug, Clone)]
936pub struct FileUpload {
937 pub filename: Option<String>,
938 pub content_type: Option<String>,
939 pub data: Bytes,
940}
941
942impl FileUpload {
943 pub fn new(data: Bytes) -> Self {
944 Self {
945 filename: None,
946 content_type: None,
947 data,
948 }
949 }
950
951 pub fn with_filename(mut self, filename: impl Into<String>) -> Self {
952 self.filename = Some(filename.into());
953 self
954 }
955
956 pub fn with_content_type(mut self, content_type: impl Into<String>) -> Self {
957 self.content_type = Some(content_type.into());
958 self
959 }
960
961 pub async fn save_to(&self, path: impl Into<PathBuf>) -> crate::error::Result<()> {
963 let path = path.into();
964 std::fs::write(&path, &self.data).map_err(|e| {
965 crate::error::WebServerError::custom(format!("Failed to save file: {}", e))
966 })
967 }
968
969 pub fn size(&self) -> usize {
971 self.data.len()
972 }
973}
974
975impl Default for FileUpload {
976 fn default() -> Self {
977 Self::new(Bytes::new())
978 }
979}
980
981#[derive(Debug, Clone)]
983pub struct MultipartForm {
984 pub fields: HashMap<String, Vec<FormValue>>,
985 parts: Vec<MultipartPart>,
986}
987
988#[derive(Debug, Clone, Default)]
990pub struct MultipartPart {
991 pub field_name: Option<String>,
993 pub filename: Option<String>,
995 pub headers: HashMap<String, String>,
997 pub content: Vec<u8>,
999}
1000
1001impl MultipartPart {
1002 pub fn new() -> Self {
1004 Self::default()
1005 }
1006}
1007
1008impl MultipartForm {
1009 pub fn new() -> Self {
1010 Self {
1011 fields: HashMap::new(),
1012 parts: Vec::new(),
1013 }
1014 }
1015
1016 pub fn add_part(&mut self, part: MultipartPart) {
1018 if let Some(field_name) = &part.field_name {
1020 let value = if let Some(filename) = &part.filename {
1021 let content_type = part
1022 .headers
1023 .get("Content-Type")
1024 .cloned()
1025 .unwrap_or_else(|| "application/octet-stream".to_string());
1026
1027 let file_upload = FileUpload::new(Bytes::from(part.content.clone()))
1028 .with_filename(filename.clone())
1029 .with_content_type(content_type);
1030
1031 FormValue::File(file_upload)
1032 } else {
1033 match String::from_utf8(part.content.clone()) {
1035 Ok(text) => FormValue::Text(text),
1036 Err(_) => FormValue::Binary(part.content.clone()),
1037 }
1038 };
1039
1040 self.fields
1041 .entry(field_name.clone())
1042 .or_default()
1043 .push(value);
1044 }
1045
1046 self.parts.push(part);
1048 }
1049
1050 pub fn get_text(&self, name: &str) -> Option<&str> {
1052 self.fields.get(name)?.first().and_then(|v| match v {
1053 FormValue::Text(text) => Some(text.as_str()),
1054 _ => None,
1055 })
1056 }
1057
1058 pub fn get_all_text(&self, name: &str) -> Vec<&str> {
1060 self.fields
1061 .get(name)
1062 .map(|values| {
1063 values
1064 .iter()
1065 .filter_map(|v| match v {
1066 FormValue::Text(text) => Some(text.as_str()),
1067 _ => None,
1068 })
1069 .collect()
1070 })
1071 .unwrap_or_default()
1072 }
1073
1074 pub fn get_file(&self, name: &str) -> Option<&FileUpload> {
1076 self.fields.get(name)?.first().and_then(|v| match v {
1077 FormValue::File(file) => Some(file),
1078 _ => None,
1079 })
1080 }
1081
1082 pub fn get_all_files(&self, name: &str) -> Vec<&FileUpload> {
1084 self.fields
1085 .get(name)
1086 .map(|values| {
1087 values
1088 .iter()
1089 .filter_map(|v| match v {
1090 FormValue::File(file) => Some(file),
1091 _ => None,
1092 })
1093 .collect()
1094 })
1095 .unwrap_or_default()
1096 }
1097
1098 pub fn add_text(&mut self, name: String, value: String) {
1100 self.fields
1101 .entry(name)
1102 .or_default()
1103 .push(FormValue::Text(value));
1104 }
1105
1106 pub fn add_file(&mut self, name: String, file: FileUpload) {
1108 self.fields
1109 .entry(name)
1110 .or_default()
1111 .push(FormValue::File(file));
1112 }
1113}
1114
1115impl Default for MultipartForm {
1116 fn default() -> Self {
1117 Self::new()
1118 }
1119}
1120
1121#[derive(Debug, Clone, PartialEq)]
1123pub enum WebSocketMessage {
1124 Text(String),
1126 Binary(Vec<u8>),
1128 Ping(Vec<u8>),
1130 Pong(Vec<u8>),
1132 Close(Option<WebSocketCloseCode>),
1134}
1135
1136#[derive(Debug, Clone, Copy, PartialEq)]
1138pub enum WebSocketCloseCode {
1139 Normal = 1000,
1141 GoingAway = 1001,
1143 ProtocolError = 1002,
1145 UnsupportedData = 1003,
1147 InvalidFramePayloadData = 1007,
1149 PolicyViolation = 1008,
1151 MessageTooBig = 1009,
1153 MandatoryExtension = 1010,
1155 InternalServerError = 1011,
1157}
1158
1159pub trait WebSocketHandler: Send + Sync + 'static {
1161 fn on_connect(&self) -> impl std::future::Future<Output = ()> + Send;
1163
1164 fn on_message(
1166 &self,
1167 message: WebSocketMessage,
1168 ) -> impl std::future::Future<Output = crate::error::Result<Option<WebSocketMessage>>> + Send;
1169
1170 fn on_close(
1172 &self,
1173 code: Option<WebSocketCloseCode>,
1174 ) -> impl std::future::Future<Output = ()> + Send;
1175}
1176
1177#[derive(Debug)]
1179pub struct WebSocketUpgrade {
1180 pub request: Request,
1182 pub key: String,
1184 pub version: String,
1186 pub protocols: Vec<String>,
1188}
1189
1190impl WebSocketUpgrade {
1191 pub fn from_request(request: Request) -> crate::error::Result<Self> {
1193 let key = request
1194 .headers
1195 .get("Sec-WebSocket-Key")
1196 .ok_or_else(|| {
1197 crate::error::WebServerError::custom("Missing Sec-WebSocket-Key header")
1198 })?
1199 .clone();
1200
1201 let version = request
1202 .headers
1203 .get("Sec-WebSocket-Version")
1204 .unwrap_or(&"13".to_string())
1205 .clone();
1206
1207 let protocols = request
1208 .headers
1209 .get("Sec-WebSocket-Protocol")
1210 .map(|s| s.split(',').map(|p| p.trim().to_string()).collect())
1211 .unwrap_or_default();
1212
1213 Ok(Self {
1214 request,
1215 key,
1216 version,
1217 protocols,
1218 })
1219 }
1220
1221 pub fn accept<H>(self, handler: H) -> WebSocketResponse<H>
1223 where
1224 H: WebSocketHandler,
1225 {
1226 WebSocketResponse {
1227 upgrade: self,
1228 handler,
1229 }
1230 }
1231
1232 pub fn generate_accept_key(&self) -> String {
1234 use base64::Engine;
1235 use sha1::{Digest, Sha1};
1236
1237 const WEBSOCKET_MAGIC_KEY: &str = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
1238
1239 let mut hasher = Sha1::new();
1240 hasher.update(self.key.as_bytes());
1241 hasher.update(WEBSOCKET_MAGIC_KEY.as_bytes());
1242 let digest = hasher.finalize();
1243
1244 base64::engine::general_purpose::STANDARD.encode(digest)
1245 }
1246}
1247
1248#[derive(Debug)]
1250pub struct WebSocketResponse<H: WebSocketHandler> {
1251 pub upgrade: WebSocketUpgrade,
1252 pub handler: H,
1253}
1254
1255