Add ability to add request headers to proxied requests
This commit is contained in:
parent
651f3f4bb7
commit
5099f79260
@ -5,7 +5,14 @@ domain:
|
|||||||
builtins:
|
builtins:
|
||||||
foo:
|
foo:
|
||||||
kind: proxy
|
kind: proxy
|
||||||
url: http://ok
|
url: http://127.0.0.1:9000
|
||||||
|
request_http_headers:
|
||||||
|
- name: x-foo
|
||||||
|
value: BAR
|
||||||
|
- name: host
|
||||||
|
value: hi
|
||||||
|
- name: user-agent
|
||||||
|
value: ""
|
||||||
bar:
|
bar:
|
||||||
kind: git
|
kind: git
|
||||||
url: a
|
url: a
|
||||||
|
13
README.md
13
README.md
@ -66,13 +66,24 @@ domain:
|
|||||||
# public: false
|
# public: false
|
||||||
|
|
||||||
# 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. Proxies are currently limited in the following ways:
|
# web-service. Requests to the backing service will automatically have
|
||||||
|
# X-Forwarded-For and (if HTTPS) X-Forwarded-Proto headers added to them.
|
||||||
|
#
|
||||||
|
# Proxies are currently limited in the following ways:
|
||||||
# * url must be to an http endpoint (not https)
|
# * url must be to an http endpoint (not https)
|
||||||
# * dns.resolver_addr is ignored and the system-wide dns is used
|
# * dns.resolver_addr is ignored and the system-wide dns is used
|
||||||
#
|
#
|
||||||
#proxy.example.com:
|
#proxy.example.com:
|
||||||
# kind: proxy
|
# kind: proxy
|
||||||
# url: "http://some.other.service.com"
|
# url: "http://some.other.service.com"
|
||||||
|
#
|
||||||
|
# # Extra headers to add to requests being proxied
|
||||||
|
# request_http_headers:
|
||||||
|
# - name: Host
|
||||||
|
# value: "yet.another.service.com"
|
||||||
|
# - name: X-HEADER-TO-DELETE
|
||||||
|
# value: ""
|
||||||
|
#
|
||||||
# public: false
|
# public: false
|
||||||
|
|
||||||
service:
|
service:
|
||||||
|
@ -79,7 +79,9 @@ async fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (domain, builtin_domain) in &config.domain.builtins {
|
for (domain, builtin_domain) in &config.domain.builtins {
|
||||||
if let domani::origin::Descr::Proxy { ref url } = builtin_domain.settings.origin_descr {
|
if let domani::origin::Descr::Proxy { ref url, .. } =
|
||||||
|
builtin_domain.settings.origin_descr
|
||||||
|
{
|
||||||
if let Err(e) = domani::origin::proxy::validate_proxy_url(url) {
|
if let Err(e) = domani::origin::proxy::validate_proxy_url(url) {
|
||||||
panic!("invalid config for builtin domain {domain}: {e}");
|
panic!("invalid config for builtin domain {domain}: {e}");
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,12 @@ use hex::ToHex;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
|
pub struct DescrProxyHttpHeader {
|
||||||
|
pub name: String,
|
||||||
|
pub value: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
#[serde(tag = "kind")]
|
#[serde(tag = "kind")]
|
||||||
/// A unique description of an origin, from where a domain might be served.
|
/// A unique description of an origin, from where a domain might be served.
|
||||||
@ -10,7 +16,10 @@ pub enum Descr {
|
|||||||
Git { url: String, branch_name: String },
|
Git { url: String, branch_name: String },
|
||||||
|
|
||||||
#[serde(rename = "proxy")]
|
#[serde(rename = "proxy")]
|
||||||
Proxy { url: String },
|
Proxy {
|
||||||
|
url: String,
|
||||||
|
request_http_headers: Vec<DescrProxyHttpHeader>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Descr {
|
impl Descr {
|
||||||
@ -29,9 +38,17 @@ impl Descr {
|
|||||||
h_update(url);
|
h_update(url);
|
||||||
h_update(branch_name);
|
h_update(branch_name);
|
||||||
}
|
}
|
||||||
Descr::Proxy { url } => {
|
Descr::Proxy {
|
||||||
|
url,
|
||||||
|
request_http_headers,
|
||||||
|
} => {
|
||||||
h_update("proxy");
|
h_update("proxy");
|
||||||
h_update(url);
|
h_update(url);
|
||||||
|
for h in request_http_headers {
|
||||||
|
h_update("header");
|
||||||
|
h_update(&h.name);
|
||||||
|
h_update(&h.value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::error::unexpected::{self, Mappable};
|
use crate::error::unexpected::{self, Mappable};
|
||||||
use crate::{domain, origin};
|
use crate::{domain, origin};
|
||||||
use http::header::HeaderValue;
|
use http::header::{HeaderName, HeaderValue};
|
||||||
use std::{net, str::FromStr};
|
use std::{net, str::FromStr};
|
||||||
|
|
||||||
// proxy is a special case because it is so tied to the underlying protocol that a request is
|
// proxy is a special case because it is so tied to the underlying protocol that a request is
|
||||||
@ -30,33 +30,37 @@ pub async fn serve_http_request(
|
|||||||
mut req: hyper::Request<hyper::Body>,
|
mut req: hyper::Request<hyper::Body>,
|
||||||
req_is_https: bool,
|
req_is_https: bool,
|
||||||
) -> unexpected::Result<hyper::Response<hyper::Body>> {
|
) -> unexpected::Result<hyper::Response<hyper::Body>> {
|
||||||
let proxy_url = if let origin::Descr::Proxy { ref url } = settings.origin_descr {
|
let (proxy_url, request_http_headers) = if let origin::Descr::Proxy {
|
||||||
url
|
ref url,
|
||||||
|
ref request_http_headers,
|
||||||
|
} = settings.origin_descr
|
||||||
|
{
|
||||||
|
(url, request_http_headers)
|
||||||
} else {
|
} else {
|
||||||
panic!("non-proxy domain settings passed in: {settings:?}")
|
panic!("non-proxy domain settings passed in: {settings:?}")
|
||||||
};
|
};
|
||||||
|
|
||||||
let parsed_proxy_url =
|
for header in request_http_headers {
|
||||||
http::Uri::from_str(proxy_url).or_unexpected_while("parsing proxy url {proxy_url}")?;
|
let name: HeaderName = header
|
||||||
|
.name
|
||||||
|
.as_str()
|
||||||
|
.try_into()
|
||||||
|
.map_unexpected_while(|| format!("parsing header name {}", &header.name))?;
|
||||||
|
|
||||||
// figure out what the host header should be, based on the host[:port] of the proxy_url
|
if header.value == "" {
|
||||||
let host = {
|
req.headers_mut().remove(name);
|
||||||
let authority = parsed_proxy_url.authority().or_unexpected_while(format!(
|
continue;
|
||||||
"getting host from proxy url {proxy_url}, there is no host"
|
}
|
||||||
))?;
|
|
||||||
|
|
||||||
let host_and_port;
|
let value = HeaderValue::from_str(&header.value).map_unexpected_while(|| {
|
||||||
let mut host = authority.host();
|
format!(
|
||||||
|
"parsing {} as value for header {}",
|
||||||
|
&header.value, &header.name
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
if let Some(port) = authority.port() {
|
req.headers_mut().insert(name, value);
|
||||||
host_and_port = format!("{host}:{port}");
|
}
|
||||||
host = host_and_port.as_str();
|
|
||||||
};
|
|
||||||
|
|
||||||
HeaderValue::from_str(host).or_unexpected()?
|
|
||||||
};
|
|
||||||
|
|
||||||
req.headers_mut().insert("host", host);
|
|
||||||
|
|
||||||
if req_is_https {
|
if req_is_https {
|
||||||
req.headers_mut()
|
req.headers_mut()
|
||||||
|
Loading…
Reference in New Issue
Block a user