web_server_abstraction/
types.rs

1//! Common types used throughout the web server abstraction.
2
3use 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/// HTTP request type
12#[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>, // Simplified for now
20    /// Path parameters extracted from route matching
21    pub path_params: HashMap<String, String>,
22    /// Parsed cookies
23    pub cookies: HashMap<String, Cookie>,
24    /// Parsed form data (URL-encoded)
25    pub form_data: Option<HashMap<String, String>>,
26    /// Parsed multipart form data
27    pub multipart: Option<MultipartForm>,
28}
29
30impl Request {
31    /// Create a new request
32    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    /// Get the path from the URI
48    pub fn path(&self) -> &str {
49        self.uri.path()
50    }
51
52    /// Get query parameters
53    pub fn query(&self) -> Option<&str> {
54        self.uri.query()
55    }
56
57    /// Get a path parameter by name
58    pub fn param(&self, name: &str) -> Option<&str> {
59        self.path_params.get(name).map(|s| s.as_str())
60    }
61
62    /// Get all path parameters
63    pub fn params(&self) -> &HashMap<String, String> {
64        &self.path_params
65    }
66
67    /// Set path parameters (used internally by routing system)
68    pub fn set_params(&mut self, params: HashMap<String, String>) {
69        self.path_params = params;
70    }
71
72    /// Parse JSON body
73    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    /// Get body as text
82    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    /// Get cookie by name
88    pub fn cookie(&self, name: &str) -> Option<&Cookie> {
89        self.cookies.get(name)
90    }
91
92    /// Get all cookies
93    pub fn cookies(&self) -> &HashMap<String, Cookie> {
94        &self.cookies
95    }
96
97    /// Parse cookies from headers
98    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    /// Get form field value
109    pub fn form(&self, name: &str) -> Option<&str> {
110        self.form_data.as_ref()?.get(name).map(|s| s.as_str())
111    }
112
113    /// Get all form data
114    pub fn form_data(&self) -> Option<&HashMap<String, String>> {
115        self.form_data.as_ref()
116    }
117
118    /// Parse URL-encoded form data
119    pub async fn parse_form(&mut self) -> crate::error::Result<()> {
120        if self.form_data.is_some() {
121            return Ok(()); // Already parsed
122        }
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(()); // Not form data
131        }
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                // Simple URL decoding for now - replace with proper implementation later
139                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    /// Get multipart form data
150    pub fn multipart(&self) -> Option<&MultipartForm> {
151        self.multipart.as_ref()
152    }
153
154    /// Parse query parameters into a HashMap
155    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                    // URL decode key and value
161                    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    /// Get a single query parameter by name
173    pub fn query_param(&self, name: &str) -> Option<String> {
174        self.query_params().get(name).cloned()
175    }
176
177    /// Check if request accepts a specific content type
178    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 // Default to accepting if no Accept header
183        }
184    }
185
186    /// Get the content type of the request
187    pub fn content_type(&self) -> Option<&str> {
188        self.headers.get("Content-Type").map(|s| s.as_str())
189    }
190
191    /// Check if this is a JSON request
192    pub fn is_json(&self) -> bool {
193        self.content_type()
194            .is_some_and(|ct| ct.contains("application/json"))
195    }
196
197    /// Check if this is a form request
198    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    /// Check if this is a multipart request
204    pub fn is_multipart(&self) -> bool {
205        self.content_type()
206            .is_some_and(|ct| ct.contains("multipart/form-data"))
207    }
208
209    /// Get remote IP address (best effort)
210    pub fn remote_addr(&self) -> Option<&str> {
211        // Check X-Forwarded-For header first (for proxies)
212        if let Some(forwarded) = self.headers.get("X-Forwarded-For") {
213            // Take the first IP in the chain
214            if let Some(first_ip) = forwarded.split(',').next() {
215                return Some(first_ip.trim());
216            }
217        }
218
219        // Check X-Real-IP header
220        if let Some(real_ip) = self.headers.get("X-Real-IP") {
221            return Some(real_ip.as_str());
222        }
223
224        // Fall back to extensions if the adapter provides it
225        self.extensions.get("remote_addr").map(|s| s.as_str())
226    }
227
228    /// Get user agent
229    pub fn user_agent(&self) -> Option<&str> {
230        self.headers.get("User-Agent").map(|s| s.as_str())
231    }
232
233    /// Get path parameter by name
234    pub fn path_param(&self, name: &str) -> Option<&str> {
235        self.path_params.get(name).map(|s| s.as_str())
236    }
237
238    /// Get all path parameters
239    pub fn path_params(&self) -> &HashMap<String, String> {
240        &self.path_params
241    }
242
243    /// Set a path parameter (used by router)
244    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    /// Parse multipart form data
249    pub async fn parse_multipart(&mut self) -> crate::error::Result<()> {
250        if self.multipart.is_some() {
251            return Ok(()); // Already parsed
252        }
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(()); // Not multipart data
261        }
262
263        // Parse boundary from Content-Type header
264        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        // Create a multipart form and parse the body
283        let mut form = MultipartForm::new();
284
285        // Get body bytes
286        let body_bytes = self.body.bytes().await?;
287
288        // Parse multipart data
289        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        // Split by lines for simpler parsing
297        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            // Check for boundaries
305            if line == boundary_start {
306                // Save previous part if exists
307                if let Some(part) = current_part.take() {
308                    form.add_part(part);
309                }
310
311                // Start new part
312                current_part = Some(MultipartPart::new());
313                parsing_headers = true;
314                part_content = Vec::new();
315            } else if line == boundary_end {
316                // Final boundary - save last part if exists
317                if let Some(part) = current_part.take() {
318                    form.add_part(part);
319                }
320                break;
321            } else if parsing_headers {
322                // Parse headers
323                if line.is_empty() {
324                    // Empty line marks end of headers
325                    parsing_headers = false;
326                } else if let Some(part) = &mut current_part {
327                    // Parse header line
328                    if let Some((name, value)) = line.split_once(":") {
329                        let name = name.trim();
330                        let value = value.trim();
331
332                        // Handle Content-Disposition specially
333                        if name.eq_ignore_ascii_case("Content-Disposition") {
334                            // Extract field name and filename
335                            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                // Part content - accumulate until next boundary
355                if let Some(_part) = &mut current_part {
356                    part_content.extend_from_slice(line.as_bytes());
357                    // Add back the CR+LF except for the last line
358                    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        // Set the parsed form
368        self.multipart = Some(form);
369
370        Ok(())
371    }
372}
373
374/// HTTP response type
375#[derive(Debug, Clone)]
376pub struct Response {
377    pub status: StatusCode,
378    pub headers: Headers,
379    pub body: Body,
380}
381
382impl Response {
383    /// Create a new response
384    pub fn new(status: StatusCode) -> Self {
385        Self {
386            status,
387            headers: Headers::new(),
388            body: Body::empty(),
389        }
390    }
391
392    /// Create a response with status 200 OK
393    pub fn ok() -> Self {
394        Self::new(StatusCode::OK)
395    }
396
397    /// Set the body
398    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    /// Set a header
407    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    /// Create a JSON response
417    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    /// Add a cookie to the response
428    pub fn cookie(mut self, cookie: Cookie) -> Self {
429        self.headers.set("Set-Cookie", cookie.to_header_value());
430        self
431    }
432
433    /// Add multiple cookies to the response
434    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    /// Create an HTML response
442    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    /// Create a plain text response
449    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    /// Create a redirect response
456    pub fn redirect(location: impl Into<String>) -> Self {
457        Self::new(StatusCode::FOUND).header("Location", location.into())
458    }
459
460    /// Create a permanent redirect response
461    pub fn redirect_permanent(location: impl Into<String>) -> Self {
462        Self::new(StatusCode::MOVED_PERMANENTLY).header("Location", location.into())
463    }
464
465    /// Create a not found response
466    pub fn not_found() -> Self {
467        Self::new(StatusCode::NOT_FOUND).body("Not Found")
468    }
469
470    /// Create a bad request response
471    pub fn bad_request(message: impl Into<String>) -> Self {
472        Self::new(StatusCode::BAD_REQUEST).body(message.into())
473    }
474
475    /// Create an internal server error response
476    pub fn internal_server_error(message: impl Into<String>) -> Self {
477        Self::new(StatusCode::INTERNAL_SERVER_ERROR).body(message.into())
478    }
479
480    /// Set cache control headers
481    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    /// Disable caching
488    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    /// Enable CORS with simple settings
500    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    /// Enable CORS with specific origin
515    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    /// Serve a file from disk
530    pub async fn file(path: impl Into<PathBuf>) -> crate::error::Result<Self> {
531        let path = path.into();
532
533        // Read file contents
534        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        // Guess content type from file extension
542        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    /// Create a download response that forces the browser to download the file
550    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    /// Set content length header based on body size
562    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/// HTTP method enum
571#[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 std::fmt::Display for HttpMethod {
601    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
602        write!(f, "{}", self.as_str())
603    }
604}
605
606impl From<Method> for HttpMethod {
607    fn from(method: Method) -> Self {
608        match method {
609            Method::GET => Self::GET,
610            Method::POST => Self::POST,
611            Method::PUT => Self::PUT,
612            Method::DELETE => Self::DELETE,
613            Method::PATCH => Self::PATCH,
614            Method::HEAD => Self::HEAD,
615            Method::OPTIONS => Self::OPTIONS,
616            Method::TRACE => Self::TRACE,
617            Method::CONNECT => Self::CONNECT,
618            _ => Self::GET, // Default fallback
619        }
620    }
621}
622
623impl From<HttpMethod> for Method {
624    fn from(method: HttpMethod) -> Self {
625        match method {
626            HttpMethod::GET => Method::GET,
627            HttpMethod::POST => Method::POST,
628            HttpMethod::PUT => Method::PUT,
629            HttpMethod::DELETE => Method::DELETE,
630            HttpMethod::PATCH => Method::PATCH,
631            HttpMethod::HEAD => Method::HEAD,
632            HttpMethod::OPTIONS => Method::OPTIONS,
633            HttpMethod::TRACE => Method::TRACE,
634            HttpMethod::CONNECT => Method::CONNECT,
635        }
636    }
637}
638
639/// HTTP status code
640#[derive(Debug, Clone, Copy, PartialEq, Eq)]
641pub struct StatusCode(pub u16);
642
643impl StatusCode {
644    pub const OK: StatusCode = StatusCode(200);
645    pub const CREATED: StatusCode = StatusCode(201);
646    pub const NO_CONTENT: StatusCode = StatusCode(204);
647    pub const MOVED_PERMANENTLY: StatusCode = StatusCode(301);
648    pub const FOUND: StatusCode = StatusCode(302);
649    pub const BAD_REQUEST: StatusCode = StatusCode(400);
650    pub const UNAUTHORIZED: StatusCode = StatusCode(401);
651    pub const FORBIDDEN: StatusCode = StatusCode(403);
652    pub const NOT_FOUND: StatusCode = StatusCode(404);
653    pub const METHOD_NOT_ALLOWED: StatusCode = StatusCode(405);
654    pub const CONFLICT: StatusCode = StatusCode(409);
655    pub const PAYLOAD_TOO_LARGE: StatusCode = StatusCode(413);
656    pub const TOO_MANY_REQUESTS: StatusCode = StatusCode(429);
657    pub const INTERNAL_SERVER_ERROR: StatusCode = StatusCode(500);
658    pub const BAD_GATEWAY: StatusCode = StatusCode(502);
659    pub const SERVICE_UNAVAILABLE: StatusCode = StatusCode(503);
660    pub const SWITCHING_PROTOCOLS: StatusCode = StatusCode(101);
661
662    pub fn as_u16(&self) -> u16 {
663        self.0
664    }
665}
666
667impl From<HttpStatusCode> for StatusCode {
668    fn from(status: HttpStatusCode) -> Self {
669        StatusCode(status.as_u16())
670    }
671}
672
673impl From<StatusCode> for HttpStatusCode {
674    fn from(status: StatusCode) -> Self {
675        HttpStatusCode::from_u16(status.0).unwrap_or(HttpStatusCode::INTERNAL_SERVER_ERROR)
676    }
677}
678
679/// HTTP headers wrapper
680#[derive(Debug, Clone)]
681pub struct Headers {
682    inner: HashMap<String, String>,
683}
684
685impl Default for Headers {
686    fn default() -> Self {
687        Self::new()
688    }
689}
690
691impl Headers {
692    pub fn new() -> Self {
693        Self {
694            inner: HashMap::new(),
695        }
696    }
697
698    pub fn insert(&mut self, key: String, value: String) {
699        self.inner.insert(key.to_lowercase(), value);
700    }
701
702    pub fn get(&self, key: &str) -> Option<&String> {
703        self.inner.get(&key.to_lowercase())
704    }
705
706    pub fn iter(&self) -> impl Iterator<Item = (&String, &String)> {
707        self.inner.iter()
708    }
709
710    /// Set a header value (replaces existing)
711    pub fn set(&mut self, key: &str, value: String) {
712        self.insert(key.to_string(), value);
713    }
714
715    /// Add a header value (for multi-value headers like Set-Cookie)
716    pub fn add(&mut self, key: &str, value: String) {
717        let key_lower = key.to_lowercase();
718        if let Some(_existing) = self.inner.get(&key_lower) {
719            // For Set-Cookie headers, we need to keep them separate
720            // For now, just replace - proper implementation would handle multiple values
721            self.inner.insert(key_lower, value);
722        } else {
723            self.inner.insert(key_lower, value);
724        }
725    }
726}
727
728impl From<HeaderMap> for Headers {
729    fn from(headers: HeaderMap) -> Self {
730        let mut inner = HashMap::new();
731        for (key, value) in headers.iter() {
732            if let Ok(value_str) = value.to_str() {
733                inner.insert(key.to_string(), value_str.to_string());
734            }
735        }
736        Self { inner }
737    }
738}
739
740impl From<Headers> for HeaderMap {
741    fn from(headers: Headers) -> Self {
742        let mut header_map = HeaderMap::new();
743        for (key, value) in headers.inner {
744            if let (Ok(name), Ok(val)) = (
745                key.parse::<http::HeaderName>(),
746                value.parse::<http::HeaderValue>(),
747            ) {
748                header_map.insert(name, val);
749            }
750        }
751        header_map
752    }
753}
754
755/// HTTP body type
756#[derive(Debug, Clone)]
757pub struct Body {
758    data: Bytes,
759}
760
761impl Body {
762    pub fn empty() -> Self {
763        Self { data: Bytes::new() }
764    }
765
766    pub fn from_bytes(bytes: Bytes) -> Self {
767        Self { data: bytes }
768    }
769
770    pub fn from_string(s: &str) -> Self {
771        Self {
772            data: Bytes::from(s.to_owned()),
773        }
774    }
775
776    pub async fn bytes(&self) -> crate::error::Result<Bytes> {
777        Ok(self.data.clone())
778    }
779
780    pub fn len(&self) -> usize {
781        self.data.len()
782    }
783
784    pub fn is_empty(&self) -> bool {
785        self.data.is_empty()
786    }
787}
788
789impl From<String> for Body {
790    fn from(s: String) -> Self {
791        Self::from_bytes(Bytes::from(s))
792    }
793}
794
795impl From<&str> for Body {
796    fn from(s: &str) -> Self {
797        Self::from_bytes(Bytes::from(s.to_string()))
798    }
799}
800
801impl From<Vec<u8>> for Body {
802    fn from(data: Vec<u8>) -> Self {
803        Self::from_bytes(Bytes::from(data))
804    }
805}
806
807impl From<Bytes> for Body {
808    fn from(bytes: Bytes) -> Self {
809        Self::from_bytes(bytes)
810    }
811}
812
813/// HTTP Cookie
814#[derive(Debug, Clone, PartialEq)]
815pub struct Cookie {
816    pub name: String,
817    pub value: String,
818    pub domain: Option<String>,
819    pub path: Option<String>,
820    pub expires: Option<SystemTime>,
821    pub max_age: Option<Duration>,
822    pub secure: bool,
823    pub http_only: bool,
824    pub same_site: Option<SameSite>,
825}
826
827#[derive(Debug, Clone, PartialEq)]
828pub enum SameSite {
829    Strict,
830    Lax,
831    None,
832}
833
834impl Cookie {
835    pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
836        Self {
837            name: name.into(),
838            value: value.into(),
839            domain: None,
840            path: None,
841            expires: None,
842            max_age: None,
843            secure: false,
844            http_only: false,
845            same_site: None,
846        }
847    }
848
849    pub fn domain(mut self, domain: impl Into<String>) -> Self {
850        self.domain = Some(domain.into());
851        self
852    }
853
854    pub fn path(mut self, path: impl Into<String>) -> Self {
855        self.path = Some(path.into());
856        self
857    }
858
859    pub fn expires(mut self, expires: SystemTime) -> Self {
860        self.expires = Some(expires);
861        self
862    }
863
864    pub fn max_age(mut self, max_age: Duration) -> Self {
865        self.max_age = Some(max_age);
866        self
867    }
868
869    pub fn secure(mut self, secure: bool) -> Self {
870        self.secure = secure;
871        self
872    }
873
874    pub fn http_only(mut self, http_only: bool) -> Self {
875        self.http_only = http_only;
876        self
877    }
878
879    pub fn same_site(mut self, same_site: SameSite) -> Self {
880        self.same_site = Some(same_site);
881        self
882    }
883
884    /// Parse cookie from header value
885    pub fn parse(header_value: &str) -> crate::error::Result<Vec<Cookie>> {
886        let mut cookies = Vec::new();
887
888        for cookie_str in header_value.split(';') {
889            let cookie_str = cookie_str.trim();
890            if let Some((name, value)) = cookie_str.split_once('=') {
891                cookies.push(Cookie::new(name.trim(), value.trim()));
892            }
893        }
894
895        Ok(cookies)
896    }
897
898    /// Convert to header value
899    pub fn to_header_value(&self) -> String {
900        let mut result = format!("{}={}", self.name, self.value);
901
902        if let Some(ref domain) = self.domain {
903            result.push_str(&format!("; Domain={}", domain));
904        }
905
906        if let Some(ref path) = self.path {
907            result.push_str(&format!("; Path={}", path));
908        }
909
910        if let Some(expires) = self.expires
911            && let Ok(duration) = expires.duration_since(SystemTime::UNIX_EPOCH)
912        {
913            result.push_str(&format!("; Expires={}", duration.as_secs()));
914        }
915
916        if let Some(max_age) = self.max_age {
917            result.push_str(&format!("; Max-Age={}", max_age.as_secs()));
918        }
919
920        if self.secure {
921            result.push_str("; Secure");
922        }
923
924        if self.http_only {
925            result.push_str("; HttpOnly");
926        }
927
928        if let Some(ref same_site) = self.same_site {
929            let same_site_str = match same_site {
930                SameSite::Strict => "Strict",
931                SameSite::Lax => "Lax",
932                SameSite::None => "None",
933            };
934            result.push_str(&format!("; SameSite={}", same_site_str));
935        }
936
937        result
938    }
939}
940
941/// Form field value
942#[derive(Debug, Clone)]
943pub enum FormValue {
944    Text(String),
945    Binary(Vec<u8>),
946    File(FileUpload),
947}
948
949/// File upload data
950#[derive(Debug, Clone)]
951pub struct FileUpload {
952    pub filename: Option<String>,
953    pub content_type: Option<String>,
954    pub data: Bytes,
955}
956
957impl FileUpload {
958    pub fn new(data: Bytes) -> Self {
959        Self {
960            filename: None,
961            content_type: None,
962            data,
963        }
964    }
965
966    pub fn with_filename(mut self, filename: impl Into<String>) -> Self {
967        self.filename = Some(filename.into());
968        self
969    }
970
971    pub fn with_content_type(mut self, content_type: impl Into<String>) -> Self {
972        self.content_type = Some(content_type.into());
973        self
974    }
975
976    /// Save file to disk
977    pub async fn save_to(&self, path: impl Into<PathBuf>) -> crate::error::Result<()> {
978        let path = path.into();
979        std::fs::write(&path, &self.data).map_err(|e| {
980            crate::error::WebServerError::custom(format!("Failed to save file: {}", e))
981        })
982    }
983
984    /// Get file size in bytes
985    pub fn size(&self) -> usize {
986        self.data.len()
987    }
988}
989
990impl Default for FileUpload {
991    fn default() -> Self {
992        Self::new(Bytes::new())
993    }
994}
995
996/// Multipart form data
997#[derive(Debug, Clone)]
998pub struct MultipartForm {
999    pub fields: HashMap<String, Vec<FormValue>>,
1000    parts: Vec<MultipartPart>,
1001}
1002
1003/// A part of a multipart form
1004#[derive(Debug, Clone, Default)]
1005pub struct MultipartPart {
1006    /// Field name from form
1007    pub field_name: Option<String>,
1008    /// Filename if this is a file upload
1009    pub filename: Option<String>,
1010    /// Headers associated with this part
1011    pub headers: HashMap<String, String>,
1012    /// Content of the part
1013    pub content: Vec<u8>,
1014}
1015
1016impl MultipartPart {
1017    /// Create a new multipart part
1018    pub fn new() -> Self {
1019        Self::default()
1020    }
1021}
1022
1023impl MultipartForm {
1024    pub fn new() -> Self {
1025        Self {
1026            fields: HashMap::new(),
1027            parts: Vec::new(),
1028        }
1029    }
1030
1031    /// Add a part to the form
1032    pub fn add_part(&mut self, part: MultipartPart) {
1033        // Add to the fields collection for easier access
1034        if let Some(field_name) = &part.field_name {
1035            let value = if let Some(filename) = &part.filename {
1036                let content_type = part
1037                    .headers
1038                    .get("Content-Type")
1039                    .cloned()
1040                    .unwrap_or_else(|| "application/octet-stream".to_string());
1041
1042                let file_upload = FileUpload::new(Bytes::from(part.content.clone()))
1043                    .with_filename(filename.clone())
1044                    .with_content_type(content_type);
1045
1046                FormValue::File(file_upload)
1047            } else {
1048                // Assume text if no filename
1049                match String::from_utf8(part.content.clone()) {
1050                    Ok(text) => FormValue::Text(text),
1051                    Err(_) => FormValue::Binary(part.content.clone()),
1052                }
1053            };
1054
1055            self.fields
1056                .entry(field_name.clone())
1057                .or_default()
1058                .push(value);
1059        }
1060
1061        // Store the original part too
1062        self.parts.push(part);
1063    }
1064
1065    /// Get first text field value
1066    pub fn get_text(&self, name: &str) -> Option<&str> {
1067        self.fields.get(name)?.first().and_then(|v| match v {
1068            FormValue::Text(text) => Some(text.as_str()),
1069            _ => None,
1070        })
1071    }
1072
1073    /// Get all text field values
1074    pub fn get_all_text(&self, name: &str) -> Vec<&str> {
1075        self.fields
1076            .get(name)
1077            .map(|values| {
1078                values
1079                    .iter()
1080                    .filter_map(|v| match v {
1081                        FormValue::Text(text) => Some(text.as_str()),
1082                        _ => None,
1083                    })
1084                    .collect()
1085            })
1086            .unwrap_or_default()
1087    }
1088
1089    /// Get first file upload
1090    pub fn get_file(&self, name: &str) -> Option<&FileUpload> {
1091        self.fields.get(name)?.first().and_then(|v| match v {
1092            FormValue::File(file) => Some(file),
1093            _ => None,
1094        })
1095    }
1096
1097    /// Get all file uploads
1098    pub fn get_all_files(&self, name: &str) -> Vec<&FileUpload> {
1099        self.fields
1100            .get(name)
1101            .map(|values| {
1102                values
1103                    .iter()
1104                    .filter_map(|v| match v {
1105                        FormValue::File(file) => Some(file),
1106                        _ => None,
1107                    })
1108                    .collect()
1109            })
1110            .unwrap_or_default()
1111    }
1112
1113    /// Add text field
1114    pub fn add_text(&mut self, name: String, value: String) {
1115        self.fields
1116            .entry(name)
1117            .or_default()
1118            .push(FormValue::Text(value));
1119    }
1120
1121    /// Add file field
1122    pub fn add_file(&mut self, name: String, file: FileUpload) {
1123        self.fields
1124            .entry(name)
1125            .or_default()
1126            .push(FormValue::File(file));
1127    }
1128}
1129
1130impl Default for MultipartForm {
1131    fn default() -> Self {
1132        Self::new()
1133    }
1134}
1135
1136/// WebSocket message types
1137#[derive(Debug, Clone, PartialEq)]
1138pub enum WebSocketMessage {
1139    /// Text message
1140    Text(String),
1141    /// Binary message
1142    Binary(Vec<u8>),
1143    /// Ping frame
1144    Ping(Vec<u8>),
1145    /// Pong frame
1146    Pong(Vec<u8>),
1147    /// Close frame
1148    Close(Option<WebSocketCloseCode>),
1149}
1150
1151/// WebSocket close codes
1152#[derive(Debug, Clone, Copy, PartialEq)]
1153pub enum WebSocketCloseCode {
1154    /// Normal closure
1155    Normal = 1000,
1156    /// Going away
1157    GoingAway = 1001,
1158    /// Protocol error
1159    ProtocolError = 1002,
1160    /// Unsupported data
1161    UnsupportedData = 1003,
1162    /// Invalid frame payload data
1163    InvalidFramePayloadData = 1007,
1164    /// Policy violation
1165    PolicyViolation = 1008,
1166    /// Message too big
1167    MessageTooBig = 1009,
1168    /// Mandatory extension
1169    MandatoryExtension = 1010,
1170    /// Internal server error
1171    InternalServerError = 1011,
1172}
1173
1174/// WebSocket connection handler
1175pub trait WebSocketHandler: Send + Sync + 'static {
1176    /// Handle a new WebSocket connection
1177    fn on_connect(&self) -> impl std::future::Future<Output = ()> + Send;
1178
1179    /// Handle an incoming message
1180    fn on_message(
1181        &self,
1182        message: WebSocketMessage,
1183    ) -> impl std::future::Future<Output = crate::error::Result<Option<WebSocketMessage>>> + Send;
1184
1185    /// Handle connection close
1186    fn on_close(
1187        &self,
1188        code: Option<WebSocketCloseCode>,
1189    ) -> impl std::future::Future<Output = ()> + Send;
1190}
1191
1192/// WebSocket upgrade request
1193#[derive(Debug)]
1194pub struct WebSocketUpgrade {
1195    /// Original HTTP request
1196    pub request: Request,
1197    /// WebSocket key for handshake
1198    pub key: String,
1199    /// WebSocket version
1200    pub version: String,
1201    /// Requested protocols
1202    pub protocols: Vec<String>,
1203}
1204
1205impl WebSocketUpgrade {
1206    /// Create a new WebSocket upgrade from an HTTP request
1207    pub fn from_request(request: Request) -> crate::error::Result<Self> {
1208        let key = request
1209            .headers
1210            .get("Sec-WebSocket-Key")
1211            .ok_or_else(|| {
1212                crate::error::WebServerError::custom("Missing Sec-WebSocket-Key header")
1213            })?
1214            .clone();
1215
1216        let version = request
1217            .headers
1218            .get("Sec-WebSocket-Version")
1219            .unwrap_or(&"13".to_string())
1220            .clone();
1221
1222        let protocols = request
1223            .headers
1224            .get("Sec-WebSocket-Protocol")
1225            .map(|s| s.split(',').map(|p| p.trim().to_string()).collect())
1226            .unwrap_or_default();
1227
1228        Ok(Self {
1229            request,
1230            key,
1231            version,
1232            protocols,
1233        })
1234    }
1235
1236    /// Accept the WebSocket upgrade
1237    pub fn accept<H>(self, handler: H) -> WebSocketResponse<H>
1238    where
1239        H: WebSocketHandler,
1240    {
1241        WebSocketResponse {
1242            upgrade: self,
1243            handler,
1244        }
1245    }
1246
1247    /// Generate the proper WebSocket accept key according to RFC 6455
1248    pub fn generate_accept_key(&self) -> String {
1249        use base64::Engine;
1250        use sha1::{Digest, Sha1};
1251
1252        const WEBSOCKET_MAGIC_KEY: &str = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
1253
1254        let mut hasher = Sha1::new();
1255        hasher.update(self.key.as_bytes());
1256        hasher.update(WEBSOCKET_MAGIC_KEY.as_bytes());
1257        let digest = hasher.finalize();
1258
1259        base64::engine::general_purpose::STANDARD.encode(digest)
1260    }
1261}
1262
1263/// WebSocket response after accepting an upgrade
1264#[derive(Debug)]
1265pub struct WebSocketResponse<H: WebSocketHandler> {
1266    pub upgrade: WebSocketUpgrade,
1267    pub handler: H,
1268}
1269
1270// Temporarily disabled until tests are updated
1271// #[cfg(test)]
1272// mod tests;