Support serve_protocols field on domain settings

This commit is contained in:
Brian Picciano 2023-07-17 20:22:22 +02:00
parent 5099f79260
commit 7049252787
5 changed files with 121 additions and 56 deletions

View File

@ -64,6 +64,12 @@ domain:
# url: "https://somewhere.com/some/repo.git" # url: "https://somewhere.com/some/repo.git"
# branch_name: main # branch_name: main
# public: false # public: false
#
# # Which protocols to serve the domain on. The given list overwrites the
# # default, which is to serve on all available protocols.
# #serve_protocols:
# #- protocol: http
# #- protocol: https
# An example built-in domain backed by a reverse-proxy to some other # An example built-in domain backed by a reverse-proxy to some other
# web-service. Requests to the backing service will automatically have # web-service. Requests to the backing service will automatically have
@ -85,6 +91,12 @@ domain:
# value: "" # value: ""
# #
# public: false # public: false
#
# # Which protocols to serve the domain on. The given list overwrites the
# # default, which is to serve on all available protocols.
# #serve_protocols:
# #- protocol: http
# #- protocol: https
service: service:

View File

@ -3,30 +3,9 @@ pub mod checker;
mod config; mod config;
pub mod manager; pub mod manager;
mod name; mod name;
mod settings;
pub mod store; pub mod store;
pub use config::*; pub use config::*;
pub use name::*; pub use name::*;
pub use settings::*;
use crate::error::unexpected::{self, Mappable};
use crate::origin;
use hex::ToHex;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
/// Defines how a domain will behave when it is accessed. These are configured by the owner of the
/// domain during setup.
pub struct Settings {
#[serde(flatten)]
pub origin_descr: origin::Descr,
}
impl Settings {
pub fn hash(&self) -> Result<String, unexpected::Error> {
let mut h = Sha256::new();
serde_json::to_writer(&mut h, self).or_unexpected()?;
Ok(h.finalize().encode_hex::<String>())
}
}

38
src/domain/settings.rs Normal file
View File

@ -0,0 +1,38 @@
use crate::error::unexpected::{self, Mappable};
use crate::origin;
use hex::ToHex;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
#[serde(tag = "protocol")]
pub enum SettingsServeProtocol {
#[serde(rename = "http")]
Http,
#[serde(rename = "https")]
Https,
}
fn default_serve_protocols() -> Vec<SettingsServeProtocol> {
vec![SettingsServeProtocol::Http, SettingsServeProtocol::Https]
}
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
/// Defines how a domain will behave when it is accessed. These are configured by the owner of the
/// domain during setup.
pub struct Settings {
#[serde(flatten)]
pub origin_descr: origin::Descr,
#[serde(default = "default_serve_protocols")]
pub serve_protocols: Vec<SettingsServeProtocol>,
}
impl Settings {
pub fn hash(&self) -> Result<String, unexpected::Error> {
let mut h = Sha256::new();
serde_json::to_writer(&mut h, self).or_unexpected()?;
Ok(h.finalize().encode_hex::<String>())
}
}

View File

@ -165,23 +165,8 @@ impl<'svc> Service {
req: Request<Body>, req: Request<Body>,
req_is_https: bool, req_is_https: bool,
) -> Response<Body> { ) -> Response<Body> {
// first check if the domain is backed by a proxy, and deal with that first let settings = match self.domain_manager.get_settings(&domain) {
match self.domain_manager.get_settings(&domain) { Ok(settings) => settings,
Ok(settings) => {
if let origin::Descr::Proxy { .. } = settings.origin_descr {
return origin::proxy::serve_http_request(
&settings,
client_ip,
req,
req_is_https,
)
.await
.unwrap_or_else(|e| {
self.internal_error(format!("proxying {domain}: {e}").as_str())
});
}
// fall out of match
}
Err(domain::manager::GetSettingsError::NotFound) => { Err(domain::manager::GetSettingsError::NotFound) => {
return self.render_error_page(404, "Domain not found"); return self.render_error_page(404, "Domain not found");
} }
@ -190,8 +175,29 @@ impl<'svc> Service {
format!("failed to fetch settings for domain {domain}: {e}").as_str(), format!("failed to fetch settings for domain {domain}: {e}").as_str(),
); );
} }
};
let allowed = settings.serve_protocols.iter().any(|p| match p {
domain::SettingsServeProtocol::Http => !req_is_https,
domain::SettingsServeProtocol::Https => req_is_https,
});
if !allowed {
return self.render_error_page(
421,
"The requested protocol is not supported by this domain",
);
} }
// if the domain is backed by a proxy then that is handled specially.
if let origin::Descr::Proxy { .. } = settings.origin_descr {
return origin::proxy::serve_http_request(&settings, client_ip, req, req_is_https)
.await
.unwrap_or_else(|e| {
self.internal_error(format!("proxying {domain}: {e}").as_str())
});
};
let mut path_owned; let mut path_owned;
let path = req.uri().path(); let path = req.uri().path();

View File

@ -6,23 +6,24 @@ use crate::{domain, error::unexpected, origin};
#[derive(Serialize, Deserialize, Default)] #[derive(Serialize, Deserialize, Default)]
pub struct FlatDomainSettings { pub struct FlatDomainSettings {
domain_setting_origin_descr_kind: Option<String>, domain_setting_origin_descr_kind: String,
domain_setting_origin_descr_git_url: Option<String>, domain_setting_origin_descr_git_url: Option<String>,
domain_setting_origin_descr_git_branch_name: Option<String>, domain_setting_origin_descr_git_branch_name: Option<String>,
domain_setting_origin_descr_proxy_url: Option<String>, domain_setting_origin_descr_proxy_url: Option<String>,
#[serde(default)]
domain_setting_serve_protocol_http: bool,
#[serde(default)]
domain_setting_serve_protocol_https: bool,
} }
impl TryFrom<FlatDomainSettings> for domain::Settings { impl TryFrom<FlatDomainSettings> for domain::Settings {
type Error = String; type Error = String;
fn try_from(v: FlatDomainSettings) -> Result<Self, Self::Error> { fn try_from(v: FlatDomainSettings) -> Result<Self, Self::Error> {
let origin_descr = match v let origin_descr = match v.domain_setting_origin_descr_kind.as_str() {
.domain_setting_origin_descr_kind
.unwrap_or("".to_string())
.as_str()
{
"git" => Ok(origin::Descr::Git { "git" => Ok(origin::Descr::Git {
url: v url: v
.domain_setting_origin_descr_git_url .domain_setting_origin_descr_git_url
@ -35,7 +36,20 @@ impl TryFrom<FlatDomainSettings> for domain::Settings {
_ => Err("invalid domain_setting_origin_descr_kind".to_string()), _ => Err("invalid domain_setting_origin_descr_kind".to_string()),
}?; }?;
Ok(Self { origin_descr }) let mut serve_protocols = Vec::<domain::SettingsServeProtocol>::default();
if v.domain_setting_serve_protocol_http {
serve_protocols.push(domain::SettingsServeProtocol::Http);
}
if v.domain_setting_serve_protocol_https {
serve_protocols.push(domain::SettingsServeProtocol::Https);
}
Ok(Self {
origin_descr,
serve_protocols,
})
} }
} }
@ -43,16 +57,32 @@ impl TryFrom<domain::Settings> for FlatDomainSettings {
type Error = unexpected::Error; type Error = unexpected::Error;
fn try_from(v: domain::Settings) -> Result<Self, Self::Error> { fn try_from(v: domain::Settings) -> Result<Self, Self::Error> {
let mut res = FlatDomainSettings::default();
match v.origin_descr { match v.origin_descr {
origin::Descr::Git { url, branch_name } => Ok(FlatDomainSettings { origin::Descr::Git { url, branch_name } => {
domain_setting_origin_descr_kind: Some("git".to_string()), res.domain_setting_origin_descr_kind = "git".to_string();
domain_setting_origin_descr_git_url: Some(url), res.domain_setting_origin_descr_git_url = Some(url);
domain_setting_origin_descr_git_branch_name: Some(branch_name), res.domain_setting_origin_descr_git_branch_name = Some(branch_name);
..Default::default() }
}), origin::Descr::Proxy { .. } => {
origin::Descr::Proxy { .. } => Err(unexpected::Error::from( return Err(unexpected::Error::from(
"proxy origins not supported for forms", "proxy origins not supported for forms",
)), ));
}
} }
for serve_protocol in v.serve_protocols {
match serve_protocol {
domain::SettingsServeProtocol::Http => {
res.domain_setting_serve_protocol_http = true
}
domain::SettingsServeProtocol::Https => {
res.domain_setting_serve_protocol_https = true
}
}
}
Ok(res)
} }
} }