br_web_server/
request.rs

1use crate::base64::{decode, sha_256};
2use crate::config::{Config};
3use crate::encoding::Encoding;
4use chrono::{DateTime, Local};
5use json::{array, object, JsonValue};
6use log::{debug, error, warn};
7use std::io::{Error, Write};
8use std::path::{Path, PathBuf};
9use std::str::Lines;
10use std::{env, fs, io};
11use std::fs::OpenOptions;
12use std::time::Instant;
13use crate::stream::Protocol;
14
15/// 请求体
16#[derive(Clone, Debug)]
17pub struct Request {
18    pub config: Config,
19    /// 协议版本
20    pub protocol: Protocol,
21    /// 当前请求类型
22    pub method: Method,
23    /// 资源标识符
24    pub uri: Uri,
25    /// header信息
26    pub header: JsonValue,
27    /// Cookie信息
28    pub cookie: JsonValue,
29    /// 请求体
30    pub body: Body,
31    /// 认证信息
32    pub authorization: Authorization,
33    /// 处理耗时 ms 毫秒
34    pub handle_time: f64,
35    /// 请求时间
36    pub datetime: String,
37    /// 请求时间戳
38    pub timestamp: i64,
39    /// 客户端IP
40    pub client_ip: String,
41    /// 代理端IP
42    pub proxy_ip: String,
43    /// 服务端IP
44    pub server_ip: String,
45    /// 升级协议
46    pub upgrade: Upgrade,
47    /// 连接的持久性
48    pub connection: Connection,
49    /// 编码
50    pub accept_encoding: Encoding,
51    start_time: Instant,
52    /// 原始请求数据
53    pub raw_data: Vec<u8>,
54    /// 是否资源
55    pub is_resource: bool,
56}
57impl Request {
58    #[must_use]
59    pub fn default(config: Config, start_time: Instant) -> Self {
60        Self {
61            config,
62            protocol: Protocol::None,
63            method: Method::None,
64            uri: Uri::default(),
65            header: object! {},
66            cookie: object! {},
67            body: Body::default(),
68            authorization: Authorization::None,
69            handle_time: 0.0,
70            start_time,
71            datetime: String::new(),
72            timestamp: 0,
73            client_ip: String::new(),
74            proxy_ip: String::new(),
75            server_ip: String::new(),
76            upgrade: Upgrade::Http,
77            connection: Connection::None,
78            accept_encoding: Encoding::None,
79            raw_data: vec![],
80            is_resource: false,
81        }
82    }
83
84    /// 获取请求行信息
85    pub fn get_request_line(&mut self, line: &str) -> Result<(), Error> {
86        let lines = line.split_whitespace().collect::<Vec<&str>>();
87        if lines.len() != 3 {
88            return Err(Error::other("请求行错误"));
89        }
90        self.method = Method::from(lines[0]);
91        self.uri = Uri::from(lines[1]);
92        self.protocol = Protocol::from(lines[2]);
93        Ok(())
94    }
95    pub fn set_request_line(&mut self, line: &str, bytes: &mut Vec<u8>) -> Result<(), Error> {
96        let lines = line.split_whitespace().collect::<Vec<&str>>();
97        if lines.len() != 3 {
98            return Err(Error::other("请求行错误"));
99        }
100        self.protocol = Protocol::from(lines[2]);
101        match self.protocol {
102            Protocol::HTTP1_0 | Protocol::HTTP1_1 => {
103                self.method = Method::from(lines[0]);
104                self.uri = Uri::from(lines[1]);
105            }
106            Protocol::HTTP2 => {
107                let res = bytes.drain(..8).collect::<Vec<u8>>();
108                if !res.eq(b"\r\nSM\r\n\r\n") {
109                    return Err(Error::other("HTTP2格式错误"));
110                }
111            }
112            Protocol::None => {}
113        }
114        Ok(())
115    }
116
117    pub fn handle(&mut self, server_ip: &str, client_ip: &str) {
118        self.server_ip = server_ip.to_string();
119        self.client_ip = client_ip.to_string();
120
121        if self.header.has_key("x-forwarded-for") {
122            self.proxy_ip = self.header["x-forwarded-for"].as_str().unwrap_or("").to_string();
123        }
124        if self.header.has_key("x-real-ip") {
125            self.client_ip = self.header["x-real-ip"].as_str().unwrap_or("").to_string();
126        }
127        self.uri.handle(self.header["host"].as_str().unwrap_or(""));
128        self.uri.scheme = if self.config.https { "https" } else { "http" }.to_string();
129
130        let local: DateTime<Local> = Local::now();
131        self.datetime = local.format("%Y-%m-%d %H:%M:%S").to_string();
132        self.timestamp = local.timestamp();
133
134        self.handle_time = self.start_time.elapsed().as_secs_f64() * 1000.0;
135
136        if self.config.debug {
137            debug!("响应时间: {}", self.handle_time);
138        }
139    }
140    /// Header处理
141    pub fn set_headers(&mut self, data: Lines) {
142        let text = unsafe { String::from_utf8_unchecked(self.raw_data.clone()) };
143        if self.config.debug {
144            debug!("\r\n=================请求信息=================\r\n{text}\r\n========================================");
145        }
146        let mut header = object! {};
147        let mut cookie = object! {};
148        let mut body = Body::default();
149
150        for text in data {
151            let (key, value) = match text.trim().find(':') {
152                None => continue,
153                Some(e) => {
154                    let key = text[..e].trim().to_lowercase().clone();
155                    let value = text[e + 1..].trim().to_string();
156                    (key, value)
157                }
158            };
159            match key.as_str() {
160                "content-type" => match value {
161                    _ if value.contains("multipart/form-data") => {
162                        let boundarys = value.split("boundary=").collect::<Vec<&str>>();
163                        body.boundary = boundarys[1..].join("");
164                        body.content_type = ContentType::from("multipart/form-data");
165                        let _ = header.insert(key.as_str(), "multipart/form-data");
166                    }
167                    _ => {
168                        let value = match value.find(';') {
169                            None => value,
170                            Some(e) => value[..e].trim().to_string(),
171                        };
172                        body.content_type = ContentType::from(value.as_str());
173                        let _ = header.insert(key.as_str(), body.content_type.str());
174                    }
175                },
176                "content-length" => {
177                    body.content_length = value.to_string().parse::<usize>().unwrap_or(0);
178                }
179                "authorization" => {
180                    self.authorization = Authorization::from(&value);
181                }
182                "cookie" => {
183                    let _ = value.split(';').collect::<Vec<&str>>().iter().map(|&x| {
184                        match x.find('=') {
185                            None => {}
186                            Some(index) => {
187                                let key = x[..index].trim().to_string();
188                                let val = x[index + 1..].trim().to_string();
189                                let _ = cookie.insert(key.as_str(), val);
190                            }
191                        }
192                        ""
193                    }).collect::<Vec<&str>>();
194                }
195                "upgrade" => {
196                    self.upgrade = Upgrade::from(&value);
197                }
198                "connection" => {
199                    self.connection = Connection::from(&value);
200                }
201                "accept-encoding" => {
202                    self.accept_encoding = Encoding::from(&value);
203                }
204                _ => {
205                    let _ = header.insert(key.as_str(), value);
206                }
207            }
208        }
209        self.header = header.clone();
210        self.cookie = cookie.clone();
211        self.body = body.clone();
212    }
213    pub fn set_http2_headers(&mut self, key: &str, value: &str) {
214        match key {
215            "content-type" => match value {
216                _ if value.contains("multipart/form-data") => {
217                    let boundarys = value.split("boundary=").collect::<Vec<&str>>();
218                    self.body.boundary = boundarys[1..].join("");
219                    self.body.content_type = ContentType::from("multipart/form-data");
220                    self.header.insert(key, "multipart/form-data").unwrap();
221                }
222                _ => {
223                    let value = match value.find(';') {
224                        None => value,
225                        Some(e) => &*value[..e].trim().to_string(),
226                    };
227                    self.body.content_type = ContentType::from(value);
228                    let _ = self.header.insert(key, self.body.content_type.str());
229                }
230            },
231            "content-length" => {
232                self.body.content_length = value.to_string().parse::<usize>().unwrap_or(0);
233            }
234            "authorization" => {
235                self.authorization = Authorization::from(value);
236            }
237            "cookie" => {
238                let _ = value.split(';').collect::<Vec<&str>>().iter().map(|&x| {
239                    match x.find('=') {
240                        None => {}
241                        Some(index) => {
242                            let key = x[..index].trim().to_string();
243                            let val = x[index + 1..].trim().to_string();
244                            let _ = self.cookie.insert(key.as_str(), val);
245                        }
246                    }
247                    ""
248                }).collect::<Vec<&str>>();
249            }
250            "upgrade" => {
251                self.upgrade = Upgrade::from(value);
252            }
253            "connection" => {
254                self.connection = Connection::from(value);
255            }
256            "accept-encoding" => {
257                self.accept_encoding = Encoding::from(value);
258            }
259            _ => {
260                self.header.insert(key, value).unwrap();
261            }
262        }
263    }
264
265    /// 保存日志
266    pub fn save_log(&mut self) -> io::Result<()> {
267        if !self.config.log {
268            return Ok(());
269        }
270        let local: DateTime<Local> = Local::now();
271        let time_dir = local.format("%Y-%m-%d-%H").to_string();
272        let time_dir = time_dir.split('-').collect::<Vec<&str>>();
273
274        let mut res = self.config.root_path.join(self.config.runtime.clone()).join("log");
275        for item in &time_dir {
276            res.push(item);
277        }
278        fs::create_dir_all(res.parent().unwrap())?;
279        let log_file = format!("{}.log", res.to_str().unwrap());
280        let mut file = OpenOptions::new()
281            // 允许写入
282            .append(true) // 追加内容到文件末尾
283            .create(true) // 如果文件不存在,则创建
284            .open(log_file)?;
285        let data = format!(
286            "[{}] {} ClientIP: {} {} {} ContentLength: {} ContentType: {} Time: {:?} ms\r\n",
287            self.datetime,
288            self.protocol.str(),
289            self.client_ip,
290            self.method.str(),
291            self.uri.url,
292            self.body.content_length,
293            self.body.content_type.str(),
294            self.handle_time
295        );
296        file.write_all(data.as_bytes())?;
297        Ok(())
298    }
299    /// 读取资源文件
300    pub fn read_resource(&mut self) -> io::Result<PathBuf> {
301        if self.uri.path != "/" {
302            let file = self.config.root_path.join(self.config.public.clone()).join(self.uri.path.trim_start_matches("/"));
303            if file.is_file() {
304                return Ok(file);
305            }
306        }
307        if let Method::GET = self.method {
308            if self.uri.path == "/" {
309                let file = self.config.root_path.join("webpage").join(self.config.webpage.clone()).join("index.html");
310                if file.is_file() {
311                    return Ok(file);
312                }
313            } else {
314                let file = self.config.root_path.join("webpage").join(self.config.webpage.clone()).join(self.uri.path.trim_start_matches("/"));
315                if file.is_file() {
316                    return Ok(file);
317                }
318            }
319        }
320        Err(Error::other("Not a file"))
321    }
322}
323
324
325/// 请求方法
326#[derive(Clone, Debug)]
327pub enum Method {
328    POST,
329    GET,
330    HEAD,
331    PUT,
332    DELETE,
333    OPTIONS,
334    PATCH,
335    TRACE,
336    VIEW,
337    PROPFIND,
338    /// http2.0
339    PRI,
340    None,
341}
342
343impl Method {
344    #[must_use]
345    pub fn from(name: &str) -> Self {
346        match name.to_lowercase().as_str() {
347            "post" => Self::POST,
348            "get" => Self::GET,
349            "head" => Self::HEAD,
350            "put" => Self::PUT,
351            "delete" => Self::DELETE,
352            "options" => Self::OPTIONS,
353            "patch" => Self::PATCH,
354            "trace" => Self::TRACE,
355            "view" => Self::VIEW,
356            "propfind" => Self::PROPFIND,
357            "pri" => Self::PRI,
358            _ => Self::None,
359        }
360    }
361    pub fn str(&mut self) -> &str {
362        match self {
363            Method::POST => "POST",
364            Method::GET => "GET",
365            Method::HEAD => "HEAD",
366            Method::PUT => "PUT",
367            Method::DELETE => "DELETE",
368            Method::OPTIONS => "OPTIONS",
369            Method::PATCH => "PATCH",
370            Method::TRACE => "TRACE",
371            Method::VIEW => "VIEW",
372            Method::PROPFIND => "PROPFIND",
373            Method::PRI => "PRI",
374            Method::None => "",
375        }
376    }
377}
378/// 统一资源标识符
379#[derive(Clone, Debug)]
380pub struct Uri {
381    /// 完整 url
382    pub url: String,
383    /// 查询字符串
384    pub query: String,
385    /// 查询参数
386    pub query_params: JsonValue,
387    /// 片段或位置
388    pub fragment: String,
389    /// 资源路径
390    pub path: String,
391    /// 资源段落
392    pub path_segments: Vec<String>,
393    /// 访问协议
394    pub scheme: String,
395    /// 主机名或 IP 地址
396    pub host: String,
397    /// 端口号
398    pub port: String,
399}
400impl Uri {
401    #[must_use]
402    pub fn from(url: &str) -> Self {
403        let mut decoded_url = Uri::decode(url).unwrap_or(url.to_string());
404        let fragment = match decoded_url.rfind('#') {
405            None => String::new(),
406            Some(index) => decoded_url.drain(index..).collect::<String>(),
407        };
408        let mut query = String::new();
409        let query_params = match decoded_url.rfind('?') {
410            None => object! {},
411            Some(index) => {
412                let text = decoded_url.drain(index..).collect::<String>();
413                query = text.trim_start_matches('?').parse().unwrap();
414                let text = query.split('&').collect::<Vec<&str>>();
415                let mut params = object! {};
416                for &item in &text {
417                    if let Some(index) = item.find('=') {
418                        let key = item[..index].to_string();
419                        let value = item[index + 1..].to_string();
420                        let _ = params.insert(
421                            Uri::decode(&key).unwrap_or(key.to_string()).as_str(),
422                            Uri::decode(&value).unwrap_or(key.to_string()),
423                        );
424                    }
425                }
426                params
427            }
428        };
429        let path_segments = decoded_url.split('/').collect::<Vec<&str>>();
430        let path_segments = path_segments.into_iter().filter(|&x| !x.is_empty()).collect::<Vec<&str>>().iter().map(|&x| x.to_string()).collect::<Vec<String>>();
431        Self {
432            url: url.to_string(),
433            query,
434            query_params,
435            fragment,
436            path: decoded_url.clone(),
437            path_segments,
438            scheme: String::new(),
439            host: String::new(),
440            port: String::new(),
441        }
442    }
443    /// 解码
444    pub fn decode(input: &str) -> Result<String, String> {
445        let mut decoded = String::new();
446        let bytes = input.as_bytes();
447        let mut i = 0;
448
449        while i < bytes.len() {
450            if bytes[i] == b'%' {
451                if i + 2 >= bytes.len() {
452                    return Err("Incomplete percent-encoding".into());
453                }
454                let hex = &input[i + 1..i + 3];
455                match u8::from_str_radix(hex, 16) {
456                    Ok(byte) => decoded.push(byte as char),
457                    Err(_) => return Err(format!("Invalid percent-encoding: %{hex}")),
458                }
459                i += 3;
460            } else if bytes[i] == b'+' {
461                decoded.push(' ');
462                i += 1;
463            } else {
464                decoded.push(bytes[i] as char);
465                i += 1;
466            }
467        }
468
469        Ok(decoded)
470    }
471    pub fn handle(&mut self, host: &str) {
472        if !host.is_empty() {
473            let hostport = host.split(':').collect::<Vec<&str>>();
474            if hostport.len() > 1 {
475                self.host = hostport[0].to_string();
476                self.port = hostport[1].to_string();
477            } else {
478                self.host = hostport[0].to_string();
479            }
480        }
481    }
482    #[must_use]
483    pub fn to_json(&self) -> JsonValue {
484        object! {
485            url: self.url.clone(),
486            query: self.query.clone(),
487            query_params: self.query_params.clone(),
488            fragment: self.fragment.clone(),
489            path: self.path.clone(),
490            path_segments: self.path_segments.clone(),
491            scheme: self.scheme.clone(),
492            host: self.host.clone(),
493            port: self.port.clone(),
494        }
495    }
496}
497
498impl Default for Uri {
499    fn default() -> Self {
500        Self {
501            url: String::new(),
502            query: String::new(),
503            query_params: object! {},
504            fragment: String::new(),
505            path: String::new(),
506            scheme: String::new(),
507            path_segments: Vec::new(),
508            host: String::new(),
509            port: String::new(),
510        }
511    }
512}
513
514/// 内容类型
515#[derive(Debug, Clone)]
516pub enum ContentType {
517    FormData,
518    FormUrlencoded,
519    Json,
520    Xml,
521    Javascript,
522    Text,
523    Html,
524    Other(String),
525}
526impl ContentType {
527    #[must_use]
528    pub fn from(name: &str) -> Self {
529        match name {
530            "multipart/form-data" => Self::FormData,
531            "application/x-www-form-urlencoded" => Self::FormUrlencoded,
532            "application/json" => Self::Json,
533            "application/xml" | "text/xml" => Self::Xml,
534            "application/javascript" => Self::Javascript,
535            "text/html" => Self::Html,
536            "text/plain" => Self::Text,
537            _ => Self::Other(name.to_string()),
538        }
539    }
540    pub fn str(&mut self) -> String {
541        match self {
542            Self::FormData => "multipart/form-data",
543            Self::FormUrlencoded => "application/x-www-form-urlencoded",
544            Self::Json => "application/json",
545            Self::Xml => "application/xml",
546            Self::Javascript => "application/javascript",
547            Self::Text => "text/plain",
548            Self::Html => "text/html",
549            Self::Other(name) => name,
550        }.to_string()
551    }
552}
553/// 认证
554#[derive(Clone, Debug)]
555pub enum Authorization {
556    Basic(String, String),
557    Bearer(String),
558    Digest(JsonValue),
559    None,
560}
561impl Authorization {
562    #[must_use]
563    pub fn from(data: &str) -> Self {
564        let authorization = data.split_whitespace().collect::<Vec<&str>>();
565        let mode = authorization[0].to_lowercase();
566        match mode.as_str() {
567            "basic" => match decode(&authorization[1].to_string().clone()) {
568                Ok(decoded) => {
569                    let text = String::from_utf8(decoded.clone()).unwrap();
570                    let text: Vec<&str> = text.split(':').collect();
571                    Self::Basic(text[0].to_string(), text[1].to_string())
572                }
573                Err(e) => {
574                    error!("{}basic认证解码错误: {}", line!(), e);
575                    Self::Basic(String::new(), String::new())
576                }
577            },
578            "bearer" => Self::Bearer(authorization[1].to_string()),
579            "digest" => {
580                let text = authorization[1..].concat().clone();
581                let text = text.split(',').collect::<Vec<&str>>();
582                let mut params = object! {};
583                for item in &text {
584                    let index = match item.find('=') {
585                        None => continue,
586                        Some(e) => e,
587                    };
588                    let key = item[..index].to_string();
589                    let value = item[index + 2..item.len() - 1].to_string();
590                    let _ = params.insert(key.as_str(), value);
591                }
592
593                Self::Digest(params)
594            }
595            _ => {
596                warn!("未知认证模式: {mode}");
597                Self::None
598            }
599        }
600    }
601    pub fn str(&mut self) -> JsonValue {
602        match self {
603            Authorization::Basic(key, value) => {
604                let mut data = object! {};
605                data[key.as_str()] = value.clone().into();
606                data
607            }
608            Authorization::Bearer(e) => e.clone().into(),
609            Authorization::Digest(e) => e.clone(),
610            Authorization::None => "".into(),
611        }
612    }
613}
614
615#[derive(Debug, Clone)]
616pub struct Body {
617    pub content_type: ContentType,
618    pub boundary: String,
619    pub content_length: usize,
620    pub content: JsonValue,
621}
622impl Body {
623    pub fn set_content(&mut self, data: Vec<u8>) {
624        match self.content_type.clone() {
625            ContentType::FormData => {
626                let mut fields = object! {};
627                let boundary_marker = format!("--{}", self.boundary);
628                let text = unsafe { String::from_utf8_unchecked(data) };
629                let parts = text.split(&boundary_marker).collect::<Vec<&str>>();
630                for part in parts {
631                    let part = part.trim();
632                    if part.is_empty() || part == "--" {
633                        continue; // 跳过无效部分
634                    }
635
636                    let mut headers_and_body = part.splitn(2, "\r\n\r\n");
637                    if let (Some(headers), Some(body)) = (headers_and_body.next(), headers_and_body.next())
638                    {
639                        // 解析头部,查找 Content-Disposition
640                        let headers = headers.split("\r\n");
641
642                        let mut field_name = "";
643                        let mut filename = "";
644                        let mut content_type = ContentType::Text;
645
646                        for header in headers {
647                            if header.to_lowercase().starts_with("content-disposition:") {
648                                match header.find("filename=\"") {
649                                    None => {}
650                                    Some(filename_start) => {
651                                        let filename_len = filename_start + 10;
652                                        let filename_end = header[filename_len..].find('"').unwrap() + filename_len;
653                                        filename = &header[filename_len..filename_end];
654                                    }
655                                }
656
657                                match header.find("name=\"") {
658                                    None => {}
659                                    Some(name_start) => {
660                                        let name_start = name_start + 6;
661                                        let name_end = header[name_start..].find('"').unwrap() + name_start;
662                                        field_name = &header[name_start..name_end];
663                                    }
664                                }
665                            }
666                            if header.to_lowercase().starts_with("content-type:") {
667                                content_type = ContentType::from(
668                                    header.to_lowercase().trim_start_matches("content-type:").trim(),
669                                );
670                            }
671                        }
672
673                        if filename.is_empty() {
674                            fields[field_name.to_string()] = JsonValue::from(body);
675                        } else {
676                            // 获取系统临时目录
677                            let mut temp_dir = env::temp_dir();
678                            // 构造临时文件的完整路径
679                            temp_dir.push(filename);
680                            // 打开(创建)临时文件
681                            let Ok(mut temp_file) = fs::File::create(&temp_dir) else { continue };
682                            if temp_file.write(body.as_bytes()).is_ok() {
683                                if fields[field_name.to_string()].is_empty() {
684                                    fields[field_name.to_string()] = array![];
685                                }
686
687                                let extension = Path::new(filename).extension() // 可能返回 None
688                                                                   .and_then(|ext| ext.to_str()); // 转换为 &str
689
690                                let suffix = extension.unwrap_or("txt");
691
692                                fields[field_name.to_string()].push(object! {
693                                        id:sha_256(body.as_bytes().to_vec()),
694                                        name:filename,
695                                        suffix:suffix,
696                                        size:body.len(),
697                                        "type":content_type.str(),
698                                        file:temp_dir.to_str()
699                                    }).unwrap();
700                            }
701                        }
702                    }
703                }
704                self.content = fields;
705            }
706            ContentType::FormUrlencoded => {
707                let text = unsafe { String::from_utf8_unchecked(data) };
708                let params = text.split('&').collect::<Vec<&str>>();
709                let mut list = object! {};
710                for param in &params {
711                    let t = param.split('=').collect::<Vec<&str>>().iter().map(|&x| Uri::decode(x).unwrap_or(x.to_string())).collect::<Vec<String>>();
712                    list[t[0].to_string()] = t[1].clone().into();
713                }
714                self.content = list;
715            }
716            ContentType::Json => {
717                let text = unsafe { String::from_utf8_unchecked(data) };
718                self.content = json::parse(text.as_str()).unwrap_or(object! {});
719            }
720            ContentType::Xml | ContentType::Html | ContentType::Text | ContentType::Javascript | ContentType::Other(_) => {
721                let text = unsafe { String::from_utf8_unchecked(data) };
722                self.content = text.into();
723            }
724        }
725    }
726}
727
728impl Default for Body {
729    fn default() -> Self {
730        Self {
731            content_type: ContentType::Other("text/plain".to_string()),
732            boundary: String::new(),
733            content_length: 0,
734            content: object! {},
735        }
736    }
737}
738/// 消息内容
739#[derive(Clone, Debug)]
740pub enum Content {
741    FormUrlencoded(JsonValue),
742    FormData(JsonValue),
743    Json(JsonValue),
744    Text(JsonValue),
745    Xml(JsonValue),
746    None,
747}
748impl Content {}
749#[derive(Clone, Debug)]
750pub enum FormData {
751    File(String, PathBuf),
752    Field(JsonValue),
753}
754
755#[derive(Clone, Debug)]
756pub enum Upgrade {
757    Websocket,
758    Http,
759    None,
760}
761impl Upgrade {
762    #[must_use]
763    pub fn from(name: &str) -> Self {
764        match name.to_lowercase().as_str() {
765            "websocket" => Self::Websocket,
766            "http" => Self::Http,
767            _ => Self::None,
768        }
769    }
770    pub fn str(&mut self) -> String {
771        match self {
772            Self::Websocket => "websocket",
773            Self::Http => "http",
774            Self::None => "",
775        }.to_string()
776    }
777}
778
779#[derive(Clone, Debug)]
780pub enum Connection {
781    KeepAlive,
782    Close,
783    Upgrade,
784    None,
785}
786impl Connection {
787    #[must_use]
788    pub fn from(value: &str) -> Self {
789        match value.to_lowercase().as_str() {
790            "upgrade" => Self::Upgrade,
791            "keep-alive" => Self::KeepAlive,
792            "close" => Self::Close,
793            _ => Self::None,
794        }
795    }
796}