use crate::{config::PathConfig, protocol::ProtocolType};
use bytes::Bytes;
use gateway_common::{error::HttpError, log::get_uuid};
use http::{
header::CONTENT_LENGTH, HeaderMap, HeaderValue, Method, Request, Response, StatusCode, Uri,
Version,
};
use http_body_util::{combinators::BoxBody, BodyExt};
use std::{collections::HashMap, convert::Infallible, sync::Arc};
use tracing::error;
type IntoRequest = (
Result<Request<BoxBody<Bytes, Infallible>>, http::Error>,
Request<BoxBody<Bytes, hyper::Error>>,
);
#[derive(PartialEq, Debug)]
pub enum DisruptionStatus {
Normal,
ToResponse,
Flush,
}
#[derive(Debug)]
pub struct HttpRequest {
request: Request<BoxBody<Bytes, hyper::Error>>,
url: Uri,
method: Method,
version: Version,
parameters: MetaData,
headers: MetaData,
bodys: Option<Bytes>,
}
impl HttpRequest {
pub async fn new(mut request: Request<BoxBody<Bytes, hyper::Error>>) -> Self {
let mut bodys = None;
if let Some(Ok(frame)) = request.body_mut().frame().await {
if frame.is_data() {
let _ = bodys.insert(frame.into_data().unwrap());
}
}
let url = request.uri().clone();
let mut parameters = MetaData::default();
if let Some(query) = url.query() {
let query_fields: Vec<&str> = query.split('&').filter(|e| !e.is_empty()).collect();
for field in query_fields {
let field: Vec<&str> = field.split('=').collect();
parameters.insert(field[0].to_owned(), field[1].to_owned())
}
}
Self {
url: request.uri().clone(),
method: request.method().clone(),
version: request.version(),
parameters,
headers: MetaData::from(request.headers()),
bodys,
request,
}
}
pub fn get_path(&self) -> &str {
self.url.path()
}
pub fn get_mut_parameters(&mut self) -> &mut MetaData {
&mut self.parameters
}
pub fn get_org_request(&self) -> &Request<BoxBody<Bytes, hyper::Error>> {
&self.request
}
pub fn get_mut_org_request(&mut self) -> &mut Request<BoxBody<Bytes, hyper::Error>> {
&mut self.request
}
pub fn into_org_request(self) -> Request<BoxBody<Bytes, hyper::Error>> {
self.request
}
pub fn into(self) -> IntoRequest {
let HttpRequest {
request,
url,
method,
version,
parameters,
headers,
bodys,
} = self;
let mut url = url.path().to_owned();
if !parameters.is_empty() {
let mut query = String::from("?");
for (key, value) in parameters.into_inner() {
query.push_str(&key);
query.push('=');
query.push_str(&value);
query.push('&');
}
url.push_str(&query[..query.len() - 1])
}
let mut builder = Request::builder().uri(url).method(method).version(version);
for (key, value) in headers.into_inner() {
builder = builder.header(key, value);
}
let body = match bodys {
Some(bytes) => bytes,
None => Bytes::new(),
};
builder = builder.header(CONTENT_LENGTH, body.len());
(
builder.body(http_body_util::Full::new(body).boxed()),
request,
)
}
}
#[derive(Debug)]
pub struct HttpResponse {
status: StatusCode,
version: Version,
headers: MetaData,
bodys: Option<Bytes>,
}
impl HttpResponse {
pub async fn new(response: &mut Response<BoxBody<Bytes, hyper::Error>>) -> Self {
let mut bodys: Option<Bytes> = None;
if let Some(Ok(frame)) = response.body_mut().frame().await {
if frame.is_data() {
let _ = bodys.insert(frame.into_data().unwrap());
}
}
Self {
status: response.status(),
version: response.version(),
headers: MetaData::from(response.headers()),
bodys,
}
}
pub fn into(self) -> Result<Response<BoxBody<Bytes, Infallible>>, http::Error> {
let HttpResponse {
status,
version,
headers,
bodys,
} = self;
let mut builder = Response::builder().status(status).version(version);
for (key, value) in headers.into_inner() {
builder = builder.header(key, value);
}
let body = match bodys {
Some(bytes) => bytes,
None => Bytes::new(),
};
builder = builder.header(CONTENT_LENGTH, body.len());
builder.body(http_body_util::Full::new(body).boxed())
}
}
#[derive(Debug)]
pub struct Context {
protocol_type: ProtocolType,
trace_id: String,
path_config: Option<Arc<PathConfig>>,
disruption: DisruptionStatus,
request: Option<HttpRequest>,
response: Result<HttpResponse, HttpError>,
}
impl Context {
pub async fn new(
protocol_type: ProtocolType,
request: Request<BoxBody<Bytes, hyper::Error>>,
) -> Self {
Self {
protocol_type,
trace_id: get_uuid(),
path_config: None,
disruption: DisruptionStatus::Normal,
request: Some(HttpRequest::new(request).await),
response: Err(HttpError::NotFind),
}
}
pub fn disruption(&mut self, status: DisruptionStatus) {
self.disruption = status
}
pub fn eq_disruption(&self, disruption: DisruptionStatus) -> bool {
self.disruption == disruption
}
pub fn insert_path_config(&mut self, path_config: Arc<PathConfig>) {
let _ = self.path_config.insert(path_config);
}
pub fn get_path_config(&self) -> Arc<PathConfig> {
self.path_config.as_ref().unwrap().clone()
}
pub fn org_request_take(&mut self) -> Request<BoxBody<Bytes, hyper::Error>> {
self.request.take().unwrap().into_org_request()
}
pub fn into_request(&mut self) -> IntoRequest {
self.request.take().unwrap().into()
}
pub async fn insert_response(&mut self, response: &mut Response<BoxBody<Bytes, hyper::Error>>) {
self.response = Ok(HttpResponse::new(response).await);
}
pub fn response_error(&mut self, http_error: HttpError) {
self.response = Err(http_error);
}
pub fn get_request(&self) -> &HttpRequest {
self.request.as_ref().unwrap()
}
pub fn get_response(&self) -> Option<&HttpResponse> {
self.response.as_ref().ok()
}
pub fn get_mut_request(&mut self) -> &mut HttpRequest {
self.request.as_mut().unwrap()
}
pub fn get_mut_response(&mut self) -> Option<&mut HttpResponse> {
self.response.as_mut().ok()
}
pub fn into_response(self) -> Result<HttpResponse, HttpError> {
self.response
}
pub fn get_protocol_type(&self) -> &ProtocolType {
&self.protocol_type
}
pub fn get_trace_id(&self) -> &str {
&self.trace_id
}
}
#[derive(Debug, Default)]
pub struct MetaData {
pub inner: HashMap<String, String>,
}
impl MetaData {
pub fn into_inner(self) -> HashMap<String, String> {
self.inner
}
pub fn insert(&mut self, key: String, value: String) {
self.inner.insert(key, value);
}
pub fn remove(&mut self, key: &str) -> Option<String> {
self.inner.remove(key)
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
}
impl From<&HeaderMap<HeaderValue>> for MetaData {
fn from(value: &HeaderMap<HeaderValue>) -> Self {
let mut inner = HashMap::new();
for (name, value) in value.iter() {
let value = match value.to_str() {
Ok(str) => str.to_owned(),
Err(error) => {
error!("http header analyze error info : {}", error);
continue;
}
};
inner.insert(name.to_string(), value);
}
Self { inner }
}
}