use std::collections::HashMap;
use std::{env, fs};
use std::io::{Error, ErrorKind, Write};
use std::path::PathBuf;
use std::str::Lines;
use chrono::{DateTime, Local};
use json::{JsonValue, object};
use log::{debug, error, warn};
use crate::base64::{decode};
use crate::config::Config;
#[derive(Clone, Debug)]
pub struct Request {
pub config: Config,
pub protocol: Protocol,
pub method: Method,
pub uri: Uri,
pub header: HashMap<String, String>,
pub cookie: HashMap<String, String>,
pub body: Body,
pub authorization: Authorization,
pub handle_time: f64,
pub datetime: String,
pub client_ip: String,
pub server_ip: String,
pub upgrade: Upgrade,
pub connection: Connection,
}
impl Request {
pub fn new(data: Vec<u8>, config: Config) -> Result<Request, Error> {
let text = unsafe { String::from_utf8_unchecked(data) };
if config.is_debug {
debug!("{}", text);
}
let lines = text.lines();
let (method, mut uri, protocol) = Request::get_request_line(lines.clone().next().unwrap().to_string().leak())?;
let (header, cookie, body, authorization, upgrade, connection) = Request::get_header(lines.clone())?;
uri.handle(header.clone());
let local: DateTime<Local> = Local::now();
let datetime = local.format("%Y-%m-%d %H:%M:%S").to_string();
Ok(Self {
config,
protocol,
method,
uri,
header,
cookie,
body,
authorization,
handle_time: Default::default(),
datetime,
client_ip: "".to_string(),
server_ip: "".to_string(),
upgrade,
connection,
})
}
pub fn get_request_line(line: &str) -> Result<(Method, Uri, Protocol), Error> {
let lines = line.split_whitespace().collect::<Vec<&str>>();
if lines.len() != 3 {
return Err(Error::new(ErrorKind::Other, "请求行错误"));
}
Ok((Method::from(lines[0]), Uri::from(lines[1]), Protocol::from(lines[2])))
}
fn get_header(mut data: Lines) -> Result<(HashMap<String, String>, HashMap<String, String>, Body, Authorization, Upgrade, Connection), Error> {
let mut header: HashMap<String, String> = HashMap::new();
let mut cookie: HashMap<String, String> = HashMap::new();
let mut body = Body::default();
let mut upgrade = Upgrade::Http;
let mut authorization = Authorization::None;
let mut connection = Connection::None;
while let Some(text) = data.next() {
let (key, value) = match text.trim().find(":") {
None => continue,
Some(e) => {
let key = text[..e].trim().to_lowercase().clone();
let value = text[e + 1..].trim().to_string();
(key, value)
}
};
match key.as_str() {
"content-type" => {
match value {
_ if value.contains("multipart/form-data") => {
let boundarys = value.split("boundary=").collect::<Vec<&str>>();
body.boundary = boundarys[1..].join("");
body.content_type = ContentType::from("multipart/form-data");
header.insert(key, "multipart/form-data".to_string());
}
_ => {
body.content_type = ContentType::from(value.as_str());
header.insert(key, body.content_type.str().to_string());
}
}
}
"content-length" => {
body.content_length = value.to_string().parse::<usize>().unwrap_or(0);
}
"authorization" => {
authorization = Authorization::from(&*value);
}
"cookie" => {
let _ = value.split(";").collect::<Vec<&str>>().iter().map(|&x| {
match x.find("=") {
None => {}
Some(index) => {
let key = x[..index].trim().to_string();
let val = x[index + 1..].trim().to_string();
cookie.insert(key, val);
}
};
""
}).collect::<Vec<&str>>();
}
"upgrade" => {
upgrade = Upgrade::from(&*value);
}
"connection" => {
connection = Connection::from(&*value);
}
_ => { header.insert(key, value); }
};
}
Ok((header, cookie, body, authorization, upgrade, connection))
}
}
impl Default for Request {
fn default() -> Self {
Self {
config: Default::default(),
protocol: Protocol::None,
method: Method::None,
uri: Uri::default(),
header: HashMap::new(),
cookie: HashMap::new(),
body: Body::default(),
authorization: Authorization::None,
handle_time: Default::default(),
datetime: "".to_string(),
client_ip: "".to_string(),
server_ip: "".to_string(),
upgrade: Upgrade::Http,
connection: Connection::None,
}
}
}
#[derive(Clone, Debug)]
pub enum Protocol {
HTTP1_1,
HTTP2,
None,
}
impl Protocol {
pub fn from(name: &str) -> Self {
match name.to_lowercase().as_str() {
"http/1.1" => Self::HTTP1_1,
"http/2" => Self::HTTP2,
_ => Self::None,
}
}
pub fn str(&mut self) -> String {
match self {
Protocol::HTTP1_1 => "HTTP/1.1",
Protocol::HTTP2 => "HTTP/2",
Protocol::None => ""
}.to_string()
}
}
#[derive(Clone, Debug)]
pub enum Method {
POST,
GET,
HEAD,
PUT,
DELETE,
OPTIONS,
PATCH,
TRACE,
None,
}
impl Method {
pub fn from(name: &str) -> Self {
match name.to_lowercase().as_str() {
"post" => Self::POST,
"get" => Self::GET,
"head" => Self::HEAD,
"put" => Self::PUT,
"delete" => Self::DELETE,
"options" => Self::OPTIONS,
"patch" => Self::PATCH,
"trace" => Self::TRACE,
_ => Self::None,
}
}
pub fn str(&mut self) -> String {
match self {
Method::POST => "POST",
Method::GET => "GET",
Method::HEAD => "HEAD",
Method::PUT => "PUT",
Method::DELETE => "DELETE",
Method::OPTIONS => "OPTIONS",
Method::PATCH => "PATCH",
Method::TRACE => "TRACE",
Method::None => ""
}.to_string()
}
}
#[derive(Clone, Debug)]
pub struct Uri {
pub url: String,
pub query: HashMap<String, String>,
pub fragment: String,
pub path: String,
pub scheme: String,
pub host: String,
pub port: String,
}
impl Uri {
pub fn from(url: &str) -> Self {
let mut uri = url.to_string();
let fragment = match uri.rfind("#") {
None => "".to_string(),
Some(index) => {
uri.drain(index..).collect::<String>()
}
};
let query = match uri.rfind("?") {
None => HashMap::new(),
Some(index) => {
let text = uri.drain(index..).collect::<String>();
let text = text.trim_start_matches("?");
let text = text.split("&").collect::<Vec<&str>>();
let mut params = HashMap::new();
for &item in text.iter() {
match item.find("=") {
None => continue,
Some(e) => {
let key = item[..e].to_string();
let value = item[e + 1..].to_string();
params.insert(Uri::decode(&*key).unwrap_or(key.to_string()), Uri::decode(&*value).unwrap_or(key.to_string()));
}
};
}
params
}
};
Self {
url: url.to_string(),
query,
fragment,
path: uri.to_string(),
scheme: "".to_string(),
host: "".to_string(),
port: "".to_string(),
}
}
pub fn decode(input: &str) -> Result<String, String> {
let mut decoded = String::new();
let bytes = input.as_bytes();
let mut i = 0;
while i < bytes.len() {
if bytes[i] == b'%' {
if i + 2 >= bytes.len() {
return Err("Incomplete percent-encoding".into());
}
let hex = &input[i + 1..i + 3];
match u8::from_str_radix(hex, 16) {
Ok(byte) => decoded.push(byte as char),
Err(_) => return Err(format!("Invalid percent-encoding: %{}", hex)),
}
i += 3;
} else if bytes[i] == b'+' {
decoded.push(' ');
i += 1;
} else {
decoded.push(bytes[i] as char);
i += 1;
}
}
Ok(decoded)
}
pub fn handle(&mut self, header: HashMap<String, String>) {
let host = header.get("host").unwrap_or(&"".to_string()).clone();
if !host.is_empty() {
let hostport = host.split(":").collect::<Vec<&str>>();
if hostport.len() > 1 {
self.host = hostport[0].to_string();
self.port = hostport[1].to_string();
} else {
self.host = hostport[0].to_string();
}
}
}
}
impl Default for Uri {
fn default() -> Self {
Self {
url: "".to_string(),
query: Default::default(),
fragment: "".to_string(),
path: "".to_string(),
scheme: "".to_string(),
host: "".to_string(),
port: "".to_string(),
}
}
}
#[derive(Debug, Clone)]
pub enum ContentType {
FormData,
FormUrlencoded,
Json,
Xml,
Javascript,
Text,
Html,
Other(String),
}
impl ContentType {
pub fn from(name: &str) -> Self {
match name {
"multipart/form-data" => Self::FormData,
"application/x-www-form-urlencoded" => Self::FormUrlencoded,
"application/json" => Self::Json,
"application/xml" | "text/xml" => Self::Xml,
"application/javascript" => Self::Javascript,
"text/html" => Self::Html,
"text/plain" => Self::Text,
_ => Self::Other(name.to_string())
}
}
pub fn str(&mut self) -> String {
match self {
Self::FormData => "multipart/form-data",
Self::FormUrlencoded => "application/x-www-form-urlencoded",
Self::Json => "application/json",
Self::Xml => "application/xml",
Self::Javascript => "application/javascript",
Self::Text => "text/plain",
Self::Html => "text/html",
Self::Other(name) => name
}.to_string()
}
}
#[derive(Clone, Debug)]
pub enum Authorization {
Basic(String, String),
Bearer(String),
Digest(HashMap<String, String>),
None,
}
impl Authorization {
pub fn from(data: &str) -> Self {
let authorization = data.split_whitespace().collect::<Vec<&str>>();
let mode = authorization[0].to_lowercase();
match mode.as_str() {
"basic" => {
match decode(&*authorization[1].to_string().clone()) {
Ok(decoded) => {
let text = String::from_utf8(decoded.clone()).unwrap();
let text: Vec<&str> = text.split(":").collect();
Self::Basic(text[0].to_string(), text[1].to_string())
}
Err(e) => {
error!("{}basic认证解码错误: {}",line!(),e);
Self::Basic("".to_string(), "".to_string())
}
}
}
"bearer" => {
Self::Bearer(authorization[1].to_string())
}
"digest" => {
let text = authorization[1..].concat().clone();
let text = text.split(",").collect::<Vec<&str>>();
let mut params = HashMap::new();
for item in text.iter() {
let index = match item.find("=") {
None => continue,
Some(e) => e,
};
let key = item[..index].to_string();
let value = item[index + 2..item.len() - 1].to_string();
params.insert(key, value);
}
Self::Digest(params)
}
_ => {
warn!("未知认证模式: {}", mode);
Self::None
}
}
}
}
#[derive(Debug, Clone)]
pub struct Body {
pub content_type: ContentType,
pub boundary: String,
pub content_length: usize,
pub content: JsonValue,
}
impl Body {
pub fn set_content(&mut self, data: Vec<u8>) {
match self.content_type.clone() {
ContentType::FormData => {
let mut fields = object! {};
let boundary_marker = format!("--{}", self.boundary);
let text = unsafe { String::from_utf8_unchecked(data) };
let parts = text.split(&boundary_marker).collect::<Vec<&str>>();
for part in parts {
let part = part.trim();
if part.is_empty() || part == "--" {
continue; }
let mut headers_and_body = part.splitn(2, "\r\n\r\n");
if let (Some(headers), Some(body)) = (headers_and_body.next(), headers_and_body.next()) {
let headers = headers.split("\r\n");
for header in headers {
if header.starts_with("Content-Disposition:") {
if let Some(filename_start) = header.find("filename=\"") {
let filename_len = filename_start + 10;
let filename_end = header[filename_len..].find('"').unwrap() + filename_len;
let filename = &header[filename_len..filename_end];
if let Some(name_start) = header.find("name=\"") {
let name_start = name_start + 6;
let name_end = header[name_start..].find('"').unwrap() + name_start;
let name = &header[name_start..name_end];
let mut temp_dir = env::temp_dir();
temp_dir.push(filename);
let mut temp_file = match fs::File::create(&temp_dir) {
Ok(e) => e,
Err(_) => continue
};
match temp_file.write(body.as_bytes()) {
Ok(_) => {
fields[name.to_string()] = object! {
name:filename,
file:temp_dir.to_str()
};
}
Err(_) => {}
};
}
} else {
if let Some(name_start) = header.find("name=\"") {
let name_start = name_start + 6;
let name_end = header[name_start..].find('"').unwrap() + name_start;
let name = &header[name_start..name_end];
fields[name.to_string()] = JsonValue::from(body);
}
}
}
}
}
}
self.content = fields.into();
}
ContentType::FormUrlencoded => {
let text = unsafe { String::from_utf8_unchecked(data) };
let params = text.split("&").collect::<Vec<&str>>();
let mut list = object! {};
for param in params.iter() {
let t = param.split("=").collect::<Vec<&str>>().iter().map(|&x| { Uri::decode(x).unwrap_or(x.to_string()) }).collect::<Vec<String>>();
list[t[0].to_string()] = t[1].clone().into();
}
self.content = list;
}
ContentType::Json => {
let text = unsafe { String::from_utf8_unchecked(data) };
self.content = json::parse(text.as_str()).unwrap_or(object! {});
}
ContentType::Xml => {
let text = unsafe { String::from_utf8_unchecked(data) };
self.content = text.into();
}
ContentType::Html | ContentType::Text | ContentType::Javascript => {
let text = unsafe { String::from_utf8_unchecked(data) };
self.content = text.into();
}
ContentType::Other(name) => {
match name.as_str() {
"application/pdf" => {
let text = unsafe { String::from_utf8_unchecked(data) };
self.content = text.into();
}
_ => {
let text = unsafe { String::from_utf8_unchecked(data) };
self.content = text.into();
}
}
}
}
}
}
impl Default for Body {
fn default() -> Self {
Self {
content_type: ContentType::Other("text/plain".to_string()),
boundary: "".to_string(),
content_length: 0,
content: object! {},
}
}
}
#[derive(Clone, Debug)]
pub enum Content {
FormUrlencoded(HashMap<String, String>),
FormData(HashMap<String, FormData>),
Json(JsonValue),
Text(JsonValue),
Xml(JsonValue),
None,
}
impl Content {}
#[derive(Clone, Debug)]
pub enum FormData {
File(String, PathBuf),
Field(JsonValue),
}
#[derive(Clone, Debug)]
pub enum Upgrade {
#[cfg(feature = "ws")]
Websocket,
Http,
None,
}
impl Upgrade {
pub fn from(name: &str) -> Self {
match name.to_lowercase().as_str() {
#[cfg(feature = "ws")]
"websocket" => Self::Websocket,
"http" => Self::Http,
_ => Self::None,
}
}
pub fn str(&mut self) -> String {
match self {
#[cfg(feature = "ws")]
Self::Websocket => "websocket",
Self::Http => "http",
Self::None => ""
}.to_string()
}
}
#[derive(Clone, Debug)]
pub enum Connection {
KeepAlive,
Close,
Upgrade,
None,
}
impl Connection {
pub fn from(value: &str) -> Self {
match value.to_lowercase().as_str() {
"upgrade" => Self::Upgrade,
"keep-alive" => Self::KeepAlive,
"close" => Self::Close,
_ => Self::None,
}
}
}