use crate::{backend::Backend, error::*, globals::Globals, log::*};
use futures::{
select,
task::{Context, Poll},
Future, FutureExt,
};
use hyper::{
client::connect::Connect,
http,
server::conn::Http,
service::{service_fn, Service},
Body, Client, HeaderMap, Method, Request, Response, StatusCode, Uri,
};
use std::{collections::HashMap, net::SocketAddr, pin::Pin, sync::Arc};
use tokio::{
io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt},
net::TcpListener,
runtime::Handle,
time::Duration,
};
// TODO: ここでbackendの名前単位でリクエストを分岐させる
pub async fn handle_request(
req: Request
,
client_ip: SocketAddr,
tls_enabled: bool,
globals: Arc,
backends: Arc>,
) -> Result> {
debug!("req: {:?}", req);
// Here we start to handle with hostname
// Find backend application for given hostname
let (hostname, _port) = parse_hostname_port(&req, tls_enabled)?;
let path = req.uri().path();
let path_and_query = req.uri().path_and_query().unwrap().as_str();
println!("{:?}", path_and_query);
let backend = if let Some(be) = backends.get(hostname.as_str()) {
be
} else {
return http_error(StatusCode::SERVICE_UNAVAILABLE);
};
// Redirect to https if tls_enabled is false and redirect_to_https is true
if !tls_enabled && backend.redirect_to_https.unwrap_or(false) {
debug!("Redirect to https: {}", hostname);
return https_redirection(hostname, globals.https_port, path_and_query);
}
// Find reverse proxy for given path
let destination_uri = if let Some(uri) = backend.reverse_proxy.destination_uris.get(path) {
uri.to_owned()
} else {
backend.reverse_proxy.default_destination_uri.clone()
};
debug!("destination_uri: {}", destination_uri);
// if req.version() == hyper::Version::HTTP_11 {
// Ok(Response::new(Body::from("Hello World")))
// } else {
// Note: it's usually better to return a Response
// with an appropriate StatusCode instead of an Err.
// Err("not HTTP/1.1, abort connection")
// http_error(StatusCode::NOT_FOUND)
https_redirection("www.google.com".to_string(), Some(443_u16), "/")
// }
// });
}
fn http_error(status_code: StatusCode) -> Result> {
let response = Response::builder()
.status(status_code)
.body(Body::empty())
.unwrap();
Ok(response)
}
fn https_redirection(
hostname: String,
https_port: Option,
path_and_query: &str,
) -> Result> {
let dest_uri: String = if let Some(https_port) = https_port {
if https_port == 443 {
format!("https://{}{}", hostname, path_and_query)
} else {
format!("https://{}:{}{}", hostname, https_port, path_and_query)
}
} else {
return http_error(StatusCode::SERVICE_UNAVAILABLE);
};
let response = Response::builder()
.status(StatusCode::MOVED_PERMANENTLY)
.header("Location", dest_uri)
.body(Body::empty())
.unwrap();
Ok(response)
}
fn parse_hostname_port(req: &Request, tls_enabled: bool) -> Result<(String, u16)> {
let hostname_port_headers = req.headers().get("host");
let hostname_uri = req.uri().host();
let port_uri = req.uri().port_u16();
if hostname_port_headers.is_none() && hostname_uri.is_none() {
bail!("No host in request header");
}
let (hostname, port) = match (hostname_uri, hostname_port_headers) {
(Some(x), _) => {
let hostname = hostname_uri.unwrap();
let port = if let Some(p) = port_uri {
p
} else if tls_enabled {
443
} else {
80
};
(hostname.to_string(), port)
}
(None, Some(x)) => {
let hp_as_uri = x.to_str().unwrap().parse::().unwrap();
let hostname = hp_as_uri
.host()
.ok_or_else(|| anyhow!("Failed to parse hostname"))?;
let port = if let Some(p) = hp_as_uri.port() {
p.as_u16()
} else if tls_enabled {
443
} else {
80
};
(hostname.to_string(), port)
}
(None, None) => {
bail!("Host unspecified in request")
}
};
Ok((hostname, port))
}