use hyper::header::{self, HeaderName, HeaderValue};
use hyper::server::conn::AddrStream;
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Request, Response, Server, StatusCode};
use log::{debug, error, info, trace, warn};
use regex::Regex;
use std::cell::{RefCell, RefMut};
use std::collections::HashMap;
use std::fmt::{Debug, Display};
use std::str::FromStr;
use std::sync::Arc;
use std::{convert::Infallible, net::SocketAddr};
use url::form_urlencoded;
use urlencoding::decode;
pub type Ctx<'a> = RefMut<'a, Context>;
pub type Json = serde_json::Value;
pub use serde_json::json;
pub use serde::Deserialize;
pub use serde::Serialize;
type Handler = dyn Fn(&mut Ctx) + 'static + Send + Sync;
#[derive(Debug, PartialEq, Eq)]
pub enum Method {
TRACE,
HEAD,
GET,
POST,
PUT,
PATCH,
DELETE,
OPTIONS,
ANY,
}
impl From<Method> for String {
fn from(v: Method) -> Self {
format!("{:?}", v)
}
}
impl From<&str> for Method {
fn from(s: &str) -> Self {
match s.to_uppercase().as_str() {
"TRACE" => Self::TRACE,
"HEAD" => Self::HEAD,
"GET" => Self::GET,
"POST" => Self::POST,
"PUT" => Self::PUT,
"PATCH" => Self::PATCH,
"DELETE" => Self::DELETE,
"OPTIONS" => Self::OPTIONS,
"ANY" => Self::ANY,
v => panic!("不存在这个类型:{}", v),
}
}
}
#[derive(Default)]
pub struct Context {
headers: HashMap<String, String>,
params: HashMap<String, String>,
queries: HashMap<String, String>,
forms: HashMap<String, String>,
datas: HashMap<String, String>,
response: Response<Body>,
is_finished: bool,
is_skip_before_filters: bool,
is_skip_after_filters: bool,
ip: String,
}
impl Context {
pub fn param<T>(&self, name: &str) -> Option<T>
where
T: FromStr,
<T as FromStr>::Err: Debug,
{
self.params
.get(name.into())
.map(|v| v.as_str().parse().unwrap())
}
pub fn query<T>(&self, name: &str) -> Option<T>
where
T: FromStr,
<T as FromStr>::Err: Debug,
{
self.queries
.get(name.into())
.map(|v| v.as_str().parse().unwrap())
}
pub fn header(&self, name: &str) -> Option<String> {
self.headers
.get(name.to_lowercase().as_str())
.map(|v| v.as_str().parse().unwrap())
}
pub fn form<T>(&self, name: &str) -> Option<T>
where
T: FromStr,
<T as FromStr>::Err: Debug,
{
self.forms
.get(name.into())
.map(|v| v.as_str().parse().unwrap())
}
pub fn data<T>(&self, name: &str) -> Option<T>
where
T: FromStr,
<T as FromStr>::Err: Debug,
{
self.datas
.get(name.into())
.map(|v| v.as_str().parse().unwrap())
}
pub fn set_data<T>(&mut self, name: &str, data: T)
where
T: FromStr + Display,
<T as FromStr>::Err: Debug,
{
if self.datas.contains_key(name) {
self.datas.remove(name);
}
self.datas.insert(name.to_string(), data.to_string());
}
pub fn ip(&self) -> String {
self.ip.clone()
}
}
impl Context {
pub fn skip_before_filters(&mut self) {
self.is_skip_before_filters = true;
}
pub fn is_skip_before_filters(&self) -> bool {
self.is_skip_before_filters
}
pub fn skip_after_filters(&mut self) {
self.is_skip_after_filters = true;
}
pub fn is_skip_after_filters(&self) -> bool {
self.is_skip_after_filters
}
pub fn is_finished(&self) -> bool {
self.is_finished
}
}
impl Context {
pub fn set_header(&mut self, name: &str, value: &str) {
let headers = self.response.headers_mut();
headers.insert(
HeaderName::from_str(name).unwrap(),
HeaderValue::from_str(value).unwrap(),
);
}
pub fn string(&mut self, data: &str) {
self.string_raw(data, "text/html; charset=utf-8");
}
pub fn text(&mut self, data: &str) {
self.string_raw(data, "text/plain; charset=utf-8");
}
#[inline]
fn string_raw(&mut self, data: &str, content_type: &str) {
self.is_finished = true;
self.set_header(header::CONTENT_TYPE.as_str(), content_type);
*self.response.body_mut() = Body::from(data.to_string());
}
pub fn json<T>(&mut self, json: T)
where
T: Serialize,
{
let data = serde_json::to_string(&json).unwrap();
self.string_raw(data.as_str(), "application/json; charset=utf-8");
}
}
#[derive(Default, Debug)]
pub struct Router {
before_fileters: Vec<Route>,
routes: Vec<Route>,
after_fileters: Vec<Route>,
has_slash: bool,
}
impl Router {
async fn shutdown_signal() {
tokio::signal::ctrl_c()
.await
.expect("安装 CTRL+C 处理器失败");
}
pub fn static_dir(path: &str, dir: &str) {}
pub fn static_file(uri: &str, filepath: &str) {}
async fn handle(
router: Arc<Router>,
addr: SocketAddr,
req: Request<Body>,
) -> Result<Response<Body>, Infallible> {
let routes = router.match_route(req.method().as_str().into(), req.uri().path());
if routes.is_none() {
let response = Response::builder()
.status(StatusCode::NOT_FOUND)
.header(header::CONTENT_TYPE, "text/html; charset=utf-8")
.body(Body::from("没匹配到路由"))
.unwrap();
return Ok(response);
}
trace!("匹配到的路由:{:?}", routes);
let r = routes.unwrap();
let mut context = Context::default();
context.ip = format!("{}:{}", addr.ip(), addr.port());
let mut tm = HashMap::new();
for (k, v) in r.params.as_ref().unwrap() {
tm.insert(k.to_string(), v.to_string());
}
context.params = tm;
let qs = req.uri().query();
if let Some(qs) = qs {
let queries = form_urlencoded::parse(qs.as_ref())
.into_owned()
.collect::<HashMap<String, String>>();
context.queries = queries;
}
for (k, v) in req.headers() {
context.headers.insert(
k.to_string().to_lowercase(),
v.to_str().unwrap_or_default().to_string(),
);
}
if let Ok(body) = hyper::body::to_bytes(req).await {
let form = form_urlencoded::parse(body.as_ref())
.into_owned()
.collect::<HashMap<String, String>>();
context.forms = form;
}
let context = RefCell::new(context);
for r in r.before {
let mut context1 = context.borrow_mut();
if context1.is_skip_before_filters || context1.is_finished() {
break;
}
(r.handler)(&mut context1);
}
if let Some(r) = r.route {
trace!("路由规则:{:?}", r);
let mut context1 = context.borrow_mut();
if !context1.is_finished() {
(r.handler)(&mut context1);
}
}
for r in r.after {
let mut context1 = context.borrow_mut();
if context1.is_finished() || context1.is_skip_after_filters {
break;
}
(r.handler)(&mut context1);
}
if context.borrow_mut().is_finished() {
Ok(Response::from(context.take().response))
} else {
let response = Response::builder()
.status(StatusCode::NOT_FOUND)
.header(header::CONTENT_TYPE, "text/html; charset=utf-8")
.body(Body::from("没匹配到处理函数"))
.unwrap();
Ok(response)
}
}
pub fn run(self, host: &str) {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let addr = match SocketAddr::from_str(host) {
Ok(v) => v,
Err(_) => {
error!("解析地址失败,请确认格式为(ip:port),你的地址是:{}", host);
return;
}
};
let router: Arc<Router> = Arc::new(self);
debug!("路由表:{:#?}", router.clone());
let make_service = make_service_fn(move |conn: &AddrStream| {
let addr = conn.remote_addr();
let router = router.clone();
let service = service_fn(move |req| Self::handle(router.clone(), addr, req));
async move { Ok::<_, Infallible>(service) }
});
let server = Server::bind(&addr).serve(make_service);
let graceful = server.with_graceful_shutdown(Self::shutdown_signal());
info!("启动成功: {}", host);
if let Err(e) = graceful.await {
eprintln!("server error: {}", e);
}
});
}
pub fn run_tls(host: &str, pem: &str, key: &str) {}
}
impl Router {
pub fn new() -> Self {
Self::default()
}
pub fn group(&mut self, path: &str) -> RouterGroup {
RouterGroup::new(path, self)
}
pub fn has_slash(&mut self) {
self.has_slash = true;
}
fn match_route(&self, method: Method, path: &str) -> Option<MatchedRoute> {
let path = if !self.has_slash && path.len() > 0 && &path[path.len() - 1..] == "/" {
&path[..path.len() - 1]
} else {
path
};
let mut params: HashMap<String, String> = HashMap::new();
trace!("查找路由:{}", path);
let mut matched_route = None;
for route in &self.routes {
if (method == route.method || route.method == Method::ANY) && route.re.is_match(path) {
matched_route = Some(route);
let cps = route.re.captures(path).unwrap();
trace!("参数列表:{:?}", route.param_names);
for name in &route.param_names {
match decode(cps.name(name.as_str()).unwrap().as_str()) {
Ok(value) => {
params.insert(name.to_string(), value.to_string());
}
Err(e) => {
warn!("路由参数值urldecode解码出错:{:?}", e)
}
}
}
break;
}
}
let mut before_filters = vec![];
for route in &self.before_fileters {
if (method == route.method || route.method == Method::ANY) && route.re.is_match(path) {
before_filters.push(route);
let cps = route.re.captures(path).unwrap();
trace!("参数列表:{:?}", route.param_names);
for name in &route.param_names {
match decode(cps.name(name.as_str()).unwrap().as_str()) {
Ok(value) => {
params.insert(name.to_string(), value.to_string());
}
Err(e) => {
warn!("路由参数值urldecode解码出错:{:?}", e)
}
}
}
}
}
trace!("路径中的参数值:{:?}", params);
let mut after_filters = vec![];
for route in &self.after_fileters {
if (method == route.method || route.method == Method::ANY) && route.re.is_match(path) {
after_filters.push(route);
}
}
if matched_route.is_none() && before_filters.len() == 0 && after_filters.len() == 0 {
return None;
}
Some(MatchedRoute {
before: before_filters,
route: matched_route,
after: after_filters,
params: Some(params),
})
}
}
impl Router {
fn add<F>(&mut self, method: Method, path: &str, handler: F)
where
F: Fn(&mut Ctx) + 'static + Send + Sync,
{
let route = Route::new(
method,
path.to_owned(),
Box::new(handler),
None,
self.has_slash,
);
self.routes.push(route);
}
pub fn before<F>(&mut self, method: Method, path: &str, handler: F)
where
F: Fn(&mut Ctx) + 'static + Send + Sync,
{
let route = Route::new_filter(
method,
path.to_owned(),
Box::new(handler),
None,
self.has_slash,
);
self.before_fileters.push(route);
}
pub fn after<F>(&mut self, method: Method, path: &str, handler: F)
where
F: Fn(&mut Ctx) + 'static + Send + Sync,
{
let route = Route::new_filter(
method,
path.to_owned(),
Box::new(handler),
None,
self.has_slash,
);
self.after_fileters.push(route);
}
pub fn get<F>(&mut self, path: &str, handler: F)
where
F: Fn(&mut Ctx) + 'static + Send + Sync,
{
self.add(Method::GET, path, handler);
}
pub fn post<F>(&mut self, path: &str, handler: F)
where
F: Fn(&mut Ctx) + 'static + Send + Sync,
{
self.add(Method::POST, path, handler);
}
pub fn trace<F>(&mut self, path: &str, handler: F)
where
F: Fn(&mut Ctx) + 'static + Send + Sync,
{
self.add(Method::TRACE, path, handler);
}
pub fn head<F>(&mut self, path: &str, handler: F)
where
F: Fn(&mut Ctx) + 'static + Send + Sync,
{
self.add(Method::HEAD, path, handler);
}
pub fn put<F>(&mut self, path: &str, handler: F)
where
F: Fn(&mut Ctx) + 'static + Send + Sync,
{
self.add(Method::PUT, path, handler);
}
pub fn patch<F>(&mut self, path: &str, handler: F)
where
F: Fn(&mut Ctx) + 'static + Send + Sync,
{
self.add(Method::PATCH, path, handler);
}
pub fn delete<F>(&mut self, path: &str, handler: F)
where
F: Fn(&mut Ctx) + 'static + Send + Sync,
{
self.add(Method::DELETE, path, handler);
}
pub fn options<F>(&mut self, path: &str, handler: F)
where
F: Fn(&mut Ctx) + 'static + Send + Sync,
{
self.add(Method::OPTIONS, path, handler);
}
pub fn any<F>(&mut self, path: &str, handler: F)
where
F: Fn(&mut Ctx) + 'static + Send + Sync,
{
self.add(Method::ANY, path, handler);
}
}
#[derive(Debug)]
struct MatchedRoute<'a> {
before: Vec<&'a Route>,
route: Option<&'a Route>,
after: Vec<&'a Route>,
params: Option<HashMap<String, String>>,
}
pub struct RouterGroup<'a> {
path: String,
router: &'a mut Router,
}
impl<'a> RouterGroup<'a> {
fn new(path: &str, router: &'a mut Router) -> Self {
Self {
path: path.to_string(),
router: router,
}
}
fn concat_path(path1: &str, path2: &str) -> String {
let l1 = path1.replace("/", "").len();
let l2 = path2.replace("/", "").len();
if l1 == 0 && l2 > 0 {
return path2.to_string();
} else if l2 == 0 && l1 > 0 {
return path1.to_string();
} else if l1 == 0 && l2 == 0 {
return "".to_string();
}
match (&path1[path1.len() - 1..], &path2[0..1]) {
("/", "/") => path1.to_string() + &path2[1..],
(p1, p2) if p1 != "/" && p2 != "/" => path1.to_string() + "/" + path2,
_ => path1.to_string() + path2,
}
}
fn add<F>(&mut self, method: Method, path: &str, handler: F)
where
F: Fn(&mut Ctx) + 'static + Send + Sync,
{
let path = Self::concat_path(self.path.as_str(), path);
self.router.add(method, path.as_str(), Box::new(handler));
}
pub fn group(&mut self, path: &str) -> RouterGroup {
let path = Self::concat_path(self.path.as_str(), path);
RouterGroup::new(path.as_str(), self.router)
}
pub fn before<F>(&mut self, method: Method, path: &str, handler: F)
where
F: Fn(&mut Ctx) + 'static + Send + Sync,
{
let path = Self::concat_path(self.path.as_str(), path);
self.router.before(method, path.as_str(), handler);
}
pub fn after<F>(&mut self, method: Method, path: &str, handler: F)
where
F: Fn(&mut Ctx) + 'static + Send + Sync,
{
let path = Self::concat_path(self.path.as_str(), path);
self.router.after(method, path.as_str(), handler);
}
pub fn get<F>(&mut self, path: &str, handler: F)
where
F: Fn(&mut Ctx) + 'static + Send + Sync,
{
self.add(Method::GET, path, handler);
}
pub fn post<F>(&mut self, path: &str, handler: F)
where
F: Fn(&mut Ctx) + 'static + Send + Sync,
{
self.add(Method::POST, path, handler);
}
pub fn trace<F>(&mut self, path: &str, handler: F)
where
F: Fn(&mut Ctx) + 'static + Send + Sync,
{
self.add(Method::TRACE, path, handler);
}
pub fn head<F>(&mut self, path: &str, handler: F)
where
F: Fn(&mut Ctx) + 'static + Send + Sync,
{
self.add(Method::HEAD, path, handler);
}
pub fn put<F>(&mut self, path: &str, handler: F)
where
F: Fn(&mut Ctx) + 'static + Send + Sync,
{
self.add(Method::PUT, path, handler);
}
pub fn patch<F>(&mut self, path: &str, handler: F)
where
F: Fn(&mut Ctx) + 'static + Send + Sync,
{
self.add(Method::PATCH, path, handler);
}
pub fn delete<F>(&mut self, path: &str, handler: F)
where
F: Fn(&mut Ctx) + 'static + Send + Sync,
{
self.add(Method::DELETE, path, handler);
}
pub fn options<F>(&mut self, path: &str, handler: F)
where
F: Fn(&mut Ctx) + 'static + Send + Sync,
{
self.add(Method::OPTIONS, path, handler);
}
pub fn any<F>(&mut self, path: &str, handler: F)
where
F: Fn(&mut Ctx) + 'static + Send + Sync,
{
self.add(Method::ANY, path, handler);
}
}
struct Route {
method: Method,
name: Option<String>,
path: String,
re: Regex,
handler: Box<Handler>,
has_slash: bool,
param_names: Vec<String>,
}
impl Debug for Route {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Route")
.field("method", &self.method)
.field("name", &self.name)
.field("path", &self.path)
.field("re", &self.re)
.field("has_slash", &self.has_slash)
.field("param_names", &self.param_names)
.finish()
}
}
impl Route {
fn new(
method: Method,
path: String,
handler: Box<Handler>,
name: Option<String>,
has_slash: bool,
) -> Self {
Self::build(method, path, handler, name, has_slash, false)
}
fn new_filter(
method: Method,
path: String,
handler: Box<Handler>,
name: Option<String>,
has_slash: bool,
) -> Self {
Self::build(method, path, handler, name, has_slash, true)
}
fn build(
method: Method,
path: String,
handler: Box<Handler>,
name: Option<String>,
has_slash: bool,
is_filter: bool,
) -> Self {
let path = if !has_slash && !path.is_empty() && &path[path.len() - 1..] == "/" {
path[..path.len() - 1].to_string()
} else {
path
};
let path = Self::path_param_type_to_regex(path.as_str());
let path_and_names = Self::path2regex(path.as_str());
trace!("路由参数列表:{:?}", path_and_names.1);
let mut re_str = format!("^{}", path_and_names.0);
if !is_filter {
re_str += "$";
}
let re = Regex::new(re_str.as_str()).unwrap();
Self {
method,
path: path.clone(),
name,
re,
handler,
has_slash,
param_names: path_and_names.1,
}
}
#[inline]
fn type_to_regex(type_name: &str) -> String {
match type_name {
"i8" => r"[\-]{0,1}\d{1,3}",
"i16" => r"[\-]{0,1}\d{1,5}",
"i32" => r"[\-]{0,1}\d{1,10}",
"i64" => r"[\-]{0,1}\d{1,19}",
"i128" => r"[\-]{0,1}\d{1,39}",
"u8" => r"\d{1,3}",
"u16" => r"\d{1,5}",
"u32" => r"\d{1,10}",
"u64" => r"\d{1,20}",
"u128" => r"\d{1,39}",
"bool" => r"true|false",
v => {
panic!("路由不支持该参数类型:{}", v);
}
}
.to_string()
}
#[inline]
fn path_param_type_to_regex(path: &str) -> String {
let mut p = String::new();
let re = Regex::new(r#"^:(?P<name>[a-zA-a_]{1}[a-zA-Z_0-9]*?):(?P<type>i32|u32|i8|u8|i64|u64|i128|u128|bool)$"#).unwrap();
for node in path.split("/") {
if node.is_empty() {
continue;
}
if re.is_match(node) {
let cms = re.captures(node).unwrap();
let name = cms.name("name").unwrap().as_str();
let tp = cms.name("type").unwrap().as_str();
let type_reg = Self::type_to_regex(tp);
p += format!("/:{}:({})", name, type_reg).as_str();
} else if &node[0..1] == ":" && &node[node.len() - 1..] != ")" {
p = p + "/" + node + r#":([\w\-%_\.~:;'"@=+,]+)"#;
} else {
p = p + "/" + node;
}
}
if path.len() > 0 && &path[path.len() - 1..] == "/" {
p += "/";
}
p
}
#[inline]
fn path2regex(path: &str) -> (String, Vec<String>) {
let mut p = String::new();
let re = Regex::new(r#"^:(?P<name>[a-zA-a_]{1}[a-zA-Z_0-9]*?):\((?P<reg>.*)\)$"#).unwrap();
let mut names = vec![];
for node in path.split("/") {
if node.is_empty() {
continue;
}
if re.is_match(node) {
let cms = re.captures(node).unwrap();
let name = cms.name("name").unwrap().as_str();
names.push(name.to_string());
p += re
.replace(node, "/(?P<${name}>${reg})")
.to_string()
.as_str();
} else {
p += "/";
p += node;
}
}
if path.len() > 0 && &path[path.len() - 1..] == "/" {
p += "/";
}
(p, names)
}
}
#[cfg(test)]
mod tests {
use std::cell::RefCell;
use std::cell::RefMut;
use std::str::FromStr;
use regex::Regex;
use crate::router::Route;
use super::Method;
use super::Router;
use super::RouterGroup;
#[test]
fn test_concat() {
assert_eq!("a/b".to_string(), RouterGroup::concat_path("a/", "/b"));
assert_eq!("a/b".to_string(), RouterGroup::concat_path("a", "/b"));
assert_eq!("a/b".to_string(), RouterGroup::concat_path("a/", "b"));
assert_eq!("a/b".to_string(), RouterGroup::concat_path("a", "b"));
assert_eq!("a".to_string(), RouterGroup::concat_path("a", ""));
assert_eq!("".to_string(), RouterGroup::concat_path("", ""));
assert_eq!("b".to_string(), RouterGroup::concat_path("", "b"));
}
#[test]
fn test_router() {
}
#[test]
fn test_router1() {
let mut r = Router::default();
let mut g = r.group("/a1/");
{
let s = "aa".to_string();
g.add(Method::GET, "/admin/login", move |c| {
let t = &s;
});
g.add(Method::DELETE, "/admin/login1", |c| {});
let mut g1 = g.group("/test1/");
{
g1.add(Method::OPTIONS, "/admin/login", |c| {});
g1.add(Method::ANY, "/admin/login1", |c| {});
}
}
for v in r.routes {
println!("{:?}", v);
}
}
#[test]
fn test_regex() {
let re = Regex::new(r"^/user/(?P<name>\w+)/(?P<id>\d{1,10})$").unwrap();
let v = re.captures("/user/zhangsan/123").unwrap();
let r = v.name("name").unwrap();
println!("{:?}", r.as_str());
let r = v.name("id").unwrap();
println!("{:?}", r.as_str());
}
#[test]
fn test_path2regex() {
let s = r#"/admin/:name:(.*+?)/info/:id:(\d+?)/name/"#;
let p = Route::path2regex(s);
assert_eq!(
r"/admin/(?P<name>.*+?)/info/(?P<id>\d+?)/name/",
p.0.as_str()
);
}
#[test]
fn test_path_param_to_regex() {
let path = "/user/:id:(.*)/:page:u32";
let p = Route::path_param_type_to_regex(path);
assert_eq!(r"/user/:id:(.*)/:page:(\d{1,10})", p.as_str());
}
#[test]
fn test_router_match() {
}
#[test]
fn test_filters() {}
#[test]
fn test_fnonce() {
struct Context {
data: String,
}
type Ctx<'a> = RefMut<'a, Context>;
type Hander = dyn Fn(Ctx) + 'static;
struct Route {
handler: Box<Hander>,
}
fn get<F>(f: F)
where
F: Fn(Ctx) + 'static,
{
let r = &Route {
handler: Box::new(f),
};
let c = RefCell::new(Context {
data: "".to_string(),
});
let v = c.borrow_mut();
(r.handler)(v);
let v = c.borrow_mut();
(r.handler)(v);
}
let s = String::new();
get(move |mut c| {
let v = &s;
c.data = "xxx".to_string();
});
}
}