add https redirection

This commit is contained in:
Jun Kurihara 2022-06-23 21:22:28 -04:00
commit ef5d1c0f2a
No known key found for this signature in database
GPG key ID: 48ADFD173ED22B03
3 changed files with 85 additions and 10 deletions

View file

@ -35,7 +35,7 @@ pub fn parse_opts(globals: &mut Globals, backends: &mut HashMap<String, Backend>
default_destination_uri: "https://google.com/".parse::<Uri>().unwrap(), default_destination_uri: "https://google.com/".parse::<Uri>().unwrap(),
destination_uris: Some(map_example), destination_uris: Some(map_example),
}, },
redirect_to_https: None, // TODO: ここはHTTPの時のみの設定。tlsの存在とは排他的。 redirect_to_https: Some(true), // TODO: ここはtlsが存在する時はSomeにすべき。Noneはtlsがないときのみのはず
tls_cert_path: Some(PathBuf::from(r"localhost1.pem")), tls_cert_path: Some(PathBuf::from(r"localhost1.pem")),
tls_cert_key_path: Some(PathBuf::from(r"localhost1.pem")), tls_cert_key_path: Some(PathBuf::from(r"localhost1.pem")),

View file

@ -9,7 +9,7 @@ use hyper::{
http, http,
server::conn::Http, server::conn::Http,
service::{service_fn, Service}, service::{service_fn, Service},
Body, Client, HeaderMap, Method, Request, Response, StatusCode, Body, Client, HeaderMap, Method, Request, Response, StatusCode, Uri,
}; };
use std::{collections::HashMap, net::SocketAddr, pin::Pin, sync::Arc}; use std::{collections::HashMap, net::SocketAddr, pin::Pin, sync::Arc};
use tokio::{ use tokio::{
@ -23,26 +23,95 @@ use tokio::{
pub async fn handle_request( pub async fn handle_request(
req: Request<Body>, req: Request<Body>,
client_ip: SocketAddr, client_ip: SocketAddr,
tls_enabled: bool,
globals: Arc<Globals>, globals: Arc<Globals>,
) -> Result<Response<Body>, http::Error> { backends: Arc<HashMap<String, Backend>>,
// http_error(StatusCode::NOT_FOUND) ) -> Result<Response<Body>> {
debug!("{:?}", req); let headers = req.headers();
// Here we start to handle with hostname
// Find backend application for given hostname
let (hostname, port) = parse_hostname_port(headers, tls_enabled)?;
let path = req.uri().path();
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) {
if let Some(https_port) = globals.https_port {
let dest = if https_port == 443 {
format!("https://{}{}", hostname, path)
} else {
format!(
"https://{}:{}{}",
hostname,
globals.https_port.unwrap(),
path
)
};
return https_redirection(dest);
} else {
return http_error(StatusCode::SERVICE_UNAVAILABLE);
}
}
// Find reverse proxy for given path
// let destination_uri = if backend.reverse_proxy.destination_uris.is_some() {
// if let (b) = backend.re
// } else {
// backend.reverse_proxy.default_destination_uri.clone();
// };
debug!("path: {}", req.uri().path());
// if req.version() == hyper::Version::HTTP_11 { // if req.version() == hyper::Version::HTTP_11 {
// Ok(Response::new(Body::from("Hello World"))) // Ok(Response::new(Body::from("Hello World")))
// } else { // } else {
// Note: it's usually better to return a Response // Note: it's usually better to return a Response
// with an appropriate StatusCode instead of an Err. // with an appropriate StatusCode instead of an Err.
// Err("not HTTP/1.1, abort connection") // Err("not HTTP/1.1, abort connection")
http_error(StatusCode::NOT_FOUND) // http_error(StatusCode::NOT_FOUND)
https_redirection("https://www.google.com/".to_string())
// } // }
// }); // });
} }
#[allow(clippy::unnecessary_wraps)] fn http_error(status_code: StatusCode) -> Result<Response<Body>> {
fn http_error(status_code: StatusCode) -> Result<Response<Body>, http::Error> {
let response = Response::builder() let response = Response::builder()
.status(status_code) .status(status_code)
.body(Body::empty()) .body(Body::empty())
.unwrap(); .unwrap();
Ok(response) Ok(response)
} }
fn https_redirection(redirect_to: String) -> Result<Response<Body>> {
let response = Response::builder()
.status(StatusCode::MOVED_PERMANENTLY)
.header("Location", redirect_to)
.body(Body::empty())
.unwrap();
Ok(response)
}
fn parse_hostname_port(headers: &HeaderMap, tls_enabled: bool) -> Result<(String, u16)> {
let hostname_port = headers
.get("host")
.ok_or_else(|| anyhow!("No host in request header"))?;
let hp_as_uri = hostname_port.to_str().unwrap().parse::<Uri>().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
};
Ok((hostname.to_string(), port))
}

View file

@ -1,7 +1,7 @@
use super::proxy_handler::handle_request; use super::proxy_handler::handle_request;
use crate::{backend::Backend, error::*, globals::Globals, log::*}; use crate::{backend::Backend, error::*, globals::Globals, log::*};
use hyper::{ use hyper::{
client::connect::Connect, server::conn::Http, service::service_fn, Body, Client, Request, client::connect::Connect, server::conn::Http, service::service_fn, Body, Client, Method, Request,
}; };
use std::{collections::HashMap, net::SocketAddr, sync::Arc}; use std::{collections::HashMap, net::SocketAddr, sync::Arc};
use tokio::{ use tokio::{
@ -65,7 +65,13 @@ where
server.serve_connection( server.serve_connection(
stream, stream,
service_fn(move |req: Request<Body>| { service_fn(move |req: Request<Body>| {
handle_request(req, peer_addr, self.globals.clone()) handle_request(
req,
peer_addr,
self.tls_enabled,
self.globals.clone(),
self.backends.clone(),
)
}), }),
), ),
) )