1use std::any::Any;
2use std::collections::{HashMap, HashSet};
3use std::convert::Infallible;
4use std::fmt::Debug;
5use std::ops::{Deref, DerefMut};
6use std::sync::Arc;
7use async_trait::async_trait;
8use http_body_util::{combinators::BoxBody, BodyExt, Empty, Full};
9use hyper::body::{Bytes, Incoming};
10use hyper::{Method, Request, Response, StatusCode};
11#[allow(unused)]
12use hyper::header::{COOKIE, HeaderValue};
13use dce_router::protocol::{Meta, RoutableProtocol};
14use dce_router::request::{Context, Request as DceRequest, Response as DceResponse};
15use dce_router::router::Router;
16use dce_router::serializer::Serialized;
17use dce_util::mixed::{DceErr, DceResult};
18use dce_router::api::Method as DceMethod;
19
20pub type HttpRaw<'a> = DceRequest<'a, HyperHttpProtocol, (), ()>;
21pub type HttpGet<'a, Dto> = DceRequest<'a, HyperHttpProtocol, (), Dto>;
22pub type HttpSame<'a, Dto> = DceRequest<'a, HyperHttpProtocol, Dto, Dto>;
23pub type Http<'a, ReqDto, RespDto> = DceRequest<'a, HyperHttpProtocol, ReqDto, RespDto>;
24
25#[derive(Debug)]
26pub struct HyperHttpProtocol {
27 meta: Meta<Request<Incoming>, Response<BoxBody<Bytes, Infallible>>>,
28}
29
30impl HyperHttpProtocol {
31 pub async fn route(
32 self,
33 router: Arc<Router<Self>>,
34 context_data: HashMap<String, Box<dyn Any + Send>>,
35 ) -> Result<Response<BoxBody<Bytes, Infallible>>, Infallible> {
36 Self::handle(self, router, context_data).await.ok_or_else(|| unreachable!("http route should always return Some(Resp)"))
37 }
38}
39
40impl From<Request<Incoming>> for HyperHttpProtocol {
41 fn from(value: Request<Incoming>) -> Self {
42 Self { meta: Meta::new(value, Default::default()) }
43 }
44}
45
46impl Into<Response<BoxBody<Bytes, Infallible>>> for HyperHttpProtocol {
47 fn into(mut self) -> Response<BoxBody<Bytes, Infallible>> {
48 let resp = self.resp_mut().take();
49 #[allow(unused_mut)]
50 let mut resp = match resp {
51 None => Response::new(Empty::new().boxed()),
52 Some(DceResponse::Serialized(sd)) => self.pack_resp(sd),
53 Some(DceResponse::Raw(rr)) => rr,
54 };
55 #[cfg(feature = "session")]
56 if let Some(resp_sid) = self.get_resp_sid() {
57 resp.headers_mut().insert("X-Session-Id", HeaderValue::from_str(resp_sid.as_str()).unwrap());
58 }
59 resp
60 }
61}
62
63impl Deref for HyperHttpProtocol {
64 type Target = Meta<Request<Incoming>, Response<BoxBody<Bytes, Infallible>>>;
65
66 fn deref(&self) -> &Self::Target {
67 &self.meta
68 }
69}
70
71impl DerefMut for HyperHttpProtocol {
72 fn deref_mut(&mut self) -> &mut Self::Target {
73 &mut self.meta
74 }
75}
76
77#[async_trait]
78impl RoutableProtocol for HyperHttpProtocol {
79 type Req = Request<Incoming>;
80 type Resp = Response<BoxBody<Bytes, Infallible>>;
81
82 async fn body(&mut self) -> DceResult<Serialized> {
83 let req = self.req_mut().take().ok_or_else(|| DceErr::closed0("Empty request"))?;
84 Ok(Serialized::Bytes(req.collect().await.or_else(DceErr::closed0_wrap)?.to_bytes()))
85 }
86
87 fn pack_resp(&self, serialized: Serialized) -> Self::Resp {
88 Response::new(match serialized {
89 Serialized::String(str) => Full::from(str).boxed(),
90 Serialized::Bytes(bytes) => Full::from(bytes).boxed(),
91 })
92 }
93
94 fn path(&self) -> &str {
95 self.req().map_or("", |r| r.uri().path().trim_start_matches('/'))
96 }
97
98 fn handle_result(self, result: DceResult<()>, _: &mut Context<Self>) -> Option<Self::Resp> {
99 Self::try_print_err(&result);
100 Some(match result {
101 Ok(_) => self.into(),
102 Err(err) => {
103 let code = err.value().code;
104 let is_openly = matches!(err, DceErr::Openly(_));
105 let mut resp = self.err_into(err);
106 if is_openly && code < 600 {
107 *resp.status_mut() = StatusCode::from_u16(code as u16).unwrap_or(StatusCode::SERVICE_UNAVAILABLE);
108 }
109 resp
110 },
111 })
112 }
113
114 #[cfg(feature = "session")]
115 fn sid(&self) -> Option<&str> {
116 self.req().map_or(None, |r| r.headers().get("X-Session-Id").map(|v| v.to_str().unwrap())
117 .or_else(|| r.headers().get(COOKIE).iter().find_map(|v| {
118 let mut cookies = v.to_str().unwrap_or("").split(';');
119 cookies.find_map(|kv| if let Some(index) = kv.find("session_id=") { Some(&kv[index + 11 ..]) } else { None } )
120 })))
121 }
122
123 fn parse_api_method(prop_mapping: &mut HashMap<&'static str, Box<dyn Any + Send + Sync>>) -> Option<Box<dyn DceMethod<Self> + Send + Sync>> {
124 Self::parse_http_method(prop_mapping)
125 }
126}
127
128
129impl HttpProtocol for HyperHttpProtocol {}
130
131impl HttpMethodGetter for HyperHttpProtocol {
132 fn method(&self) -> &Method {
133 self.req().map_or_else(|_| &Method::GET, |r| r.method())
134 }
135}
136
137pub trait HttpProtocol: RoutableProtocol + HttpMethodGetter {
138 fn parse_http_method(prop_mapping: &mut HashMap<&'static str, Box<dyn Any + Send + Sync>>) -> Option<Box<dyn DceMethod<Self> + Send + Sync>> {
139 Some(Box::new(prop_mapping.remove("method").map(|ms| if ms.is::<Method>() {
140 ms.downcast::<Method>().map(|m| HashSet::from([*m])).ok()
141 } else {
142 ms.downcast::<Vec<Method>>().map(|m| m.into_iter().collect::<HashSet<_>>()).ok()
143 }).unwrap_or_else(|| Some(HashSet::from([Method::GET, Method::HEAD, Method::OPTIONS]))).map(HttpMethodSet)?))
144 }
145}
146
147#[allow(non_snake_case, non_upper_case_globals)]
148pub mod HttpMethod {
149 use hyper::Method;
150
151 pub const Get: Method = Method::GET;
152 pub const Post: Method = Method::POST;
153 pub const Put: Method = Method::PUT;
154 pub const Delete: Method = Method::DELETE;
155 pub const Head: Method = Method::HEAD;
156 pub const Options: Method = Method::OPTIONS;
157 pub const Connect: Method = Method::CONNECT;
158 pub const Patch: Method = Method::PATCH;
159 pub const Trace: Method = Method::TRACE;
160}
161
162#[derive(Debug)]
163pub struct HttpMethodSet(HashSet<Method>);
164
165impl<T: HttpMethodGetter> DceMethod<T> for HttpMethodSet {
166 fn to_string(&self) -> String {
167 format!("[{}]", self.0.iter().map(|m| m.to_string()).fold(String::new(), |a, b| format!("{a}, {b}")))
168 }
169
170 fn req_match(&self, raw: &T) -> bool {
171 self.0.contains(raw.method())
172 }
173}
174
175pub trait HttpMethodGetter {
176 fn method(&self) -> &Method;
177}