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#[derive(Clone, Debug)]
17pub struct Request {
18 pub config: Config,
19 pub protocol: Protocol,
21 pub method: Method,
23 pub uri: Uri,
25 pub header: JsonValue,
27 pub cookie: JsonValue,
29 pub body: Body,
31 pub authorization: Authorization,
33 pub handle_time: f64,
35 pub datetime: String,
37 pub timestamp: i64,
39 pub client_ip: String,
41 pub proxy_ip: String,
43 pub server_ip: String,
45 pub upgrade: Upgrade,
47 pub connection: Connection,
49 pub accept_encoding: Encoding,
51 start_time: Instant,
52 pub raw_data: Vec<u8>,
54 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 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 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 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 .append(true) .create(true) .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 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#[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 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#[derive(Clone, Debug)]
380pub struct Uri {
381 pub url: String,
383 pub query: String,
385 pub query_params: JsonValue,
387 pub fragment: String,
389 pub path: String,
391 pub path_segments: Vec<String>,
393 pub scheme: String,
395 pub host: String,
397 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 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#[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#[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; }
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 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 let mut temp_dir = env::temp_dir();
678 temp_dir.push(filename);
680 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() .and_then(|ext| ext.to_str()); 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 ¶ms {
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#[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}