Skip to content

Commit

Permalink
Merge branch 'script-name'
Browse files Browse the repository at this point in the history
Closes #42, #41.
  • Loading branch information
mbr committed Jan 19, 2024
2 parents e6e903f + 1981ec2 commit 1031478
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 39 deletions.
38 changes: 10 additions & 28 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 7 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@ license = "MIT"

[dependencies]
anyhow = "1.0.75"
axum = { version = "0.7.1", features = [ "tracing" ] }
axum = { version = "0.7.4", features = [ "tracing" ] }
base64 = "0.21.5"
constant_time_eq = "0.3.0"
futures = "0.3.29"
gethostname = "0.4.3"
hex = "0.4.3"
itertools = "0.12.0"
nom = "7.1.3"
reqwest = { version = "0.11.23", default-features = false }
sec = { version = "1.0.0", features = [ "deserialize", "serialize" ] }
Expand All @@ -23,7 +22,12 @@ serde_json = "1.0.108"
sha2 = "0.10.8"
tempfile = "3.9.0"
thiserror = "1.0.50"
tokio = { version = "1.34.0", features = [ "rt-multi-thread", "macros", "fs", "process" ] }
tokio = { version = "1.34.0", features = [
"rt-multi-thread",
"macros",
"fs",
"process",
] }
tokio-util = { version = "0.7.10", features = [ "io" ] }
toml = "0.8.8"
tower-http = { version = "0.5.0", features = [ "trace" ] }
Expand Down
4 changes: 4 additions & 0 deletions quick-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,7 @@ podman login --tls-verify=false --username devuser --password devpw http://${REG
podman pull crccheck/hello-world
podman tag crccheck/hello-world ${REGISTRY_ADDR}/testing/hello:prod
podman push --tls-verify=false ${REGISTRY_ADDR}/testing/hello:prod

sleep 2

curl -v http://${REGISTRY_ADDR}/testing/hello
3 changes: 2 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::{
};

use anyhow::Context;
use axum::Router;
use axum::{extract::DefaultBodyLimit, Router};

use gethostname::gethostname;
use registry::ContainerRegistry;
Expand Down Expand Up @@ -86,6 +86,7 @@ async fn main() -> anyhow::Result<()> {
let app = Router::new()
.merge(registry.make_router())
.merge(reverse_proxy.make_router())
.layer(DefaultBodyLimit::max(1024 * 1024)) // See #43.
.layer(TraceLayer::new_for_http());

let listener = tokio::net::TcpListener::bind(cfg.reverse_proxy.http_bind)
Expand Down
66 changes: 59 additions & 7 deletions src/reverse_proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ use axum::{
response::{IntoResponse, Response},
RequestExt, Router,
};
use itertools::Itertools;
use tokio::sync::RwLock;
use tracing::{info, trace, warn};

Expand Down Expand Up @@ -110,6 +109,7 @@ impl PartialEq<String> for Domain {
enum Destination {
ReverseProxied {
uri: Uri,
script_name: Option<String>,
config: Arc<RuntimeConfig>,
},
Internal(Uri),
Expand Down Expand Up @@ -167,6 +167,7 @@ impl RoutingTable {
);
return Destination::ReverseProxied {
uri: Uri::from_parts(parts).expect("should not have invalidated Uri"),
script_name: None,
config: pc.config().clone(),
};
}
Expand Down Expand Up @@ -200,6 +201,7 @@ impl RoutingTable {

return Destination::ReverseProxied {
uri: Uri::from_parts(parts).unwrap(),
script_name: Some(format!("/{}", image_location)),
config: pc.config().clone(),
};
}
Expand All @@ -220,6 +222,7 @@ enum AppError {
status: StatusCode,
},
InvalidPayload,
BodyReadError(axum::Error),
Internal(anyhow::Error),
}

Expand All @@ -233,6 +236,7 @@ impl Display for AppError {
AppError::NonUtf8Header => f.write_str("a header contained non-utf8 data"),
AppError::AuthFailure { .. } => f.write_str("authentication missing or not present"),
AppError::InvalidPayload => f.write_str("invalid payload"),
AppError::BodyReadError(err) => write!(f, "could not read body: {}", err),
AppError::Internal(err) => Display::fmt(err, f),
}
}
Expand Down Expand Up @@ -262,6 +266,8 @@ impl IntoResponse for AppError {
.body(Body::empty())
.expect("should never fail to build auth failure response"),
AppError::InvalidPayload => StatusCode::BAD_REQUEST.into_response(),
// TODO: Could probably be more specific here instead of just `BAD_REQUEST`:
AppError::BodyReadError(_) => StatusCode::BAD_REQUEST.into_response(),
AppError::Internal(err) => {
(StatusCode::INTERNAL_SERVER_ERROR, err.to_string()).into_response()
}
Expand Down Expand Up @@ -315,7 +321,12 @@ fn split_path_base_url(uri: &Uri) -> Option<(ImageLocation, String)> {

// Now create the path, format is: '' / repository / image / ...
// Need to skip the first three.
let remainder = segments.join("/");
let mut remainder = String::new();

segments.for_each(|segment| {
remainder.push('/');
remainder.push_str(segment);
});

Some((image_location, remainder))
}
Expand All @@ -330,7 +341,11 @@ async fn route_request(
};

match dest_uri {
Destination::ReverseProxied { uri: dest, config } => {
Destination::ReverseProxied {
uri: dest,
script_name,
config,
} => {
trace!(%dest, "reverse proxying");

// First, check if http authentication is enabled.
Expand All @@ -356,7 +371,40 @@ async fn route_request(
let method = request.method().to_string().parse().map_err(|_| {
AppError::AssertionFailed("method http version mismatch workaround failed")
})?;
let response = rp.client.request(method, dest.to_string()).send().await;

let mut req = rp.client.request(method, dest.to_string());

for (name, value) in request.headers() {
let name: reqwest::header::HeaderName = if let Ok(name) = name.as_str().parse() {
name
} else {
continue;
};

if !BLACKLISTED.contains(&name) && !HOP_BY_HOP.contains(&name) {
if let Ok(value) = value.to_str() {
req = req.header(name, value);
} else {
continue;
}
}
}

// Attach script name.
if let Some(script_name) = script_name {
req = req.header("X-Script-Name", script_name);
};

// Retrieve body.
let request_body = axum::body::to_bytes(
request.into_limited_body(),
1024 * 1024, // See #43.
)
.await
.map_err(AppError::BodyReadError)?;
req = req.body(request_body);

let response = req.send().await;

match response {
Ok(response) => {
Expand All @@ -371,7 +419,9 @@ async fn route_request(

bld = bld.header(key_string, value_str);
}
Ok(bld.body(Body::from(response.bytes().await?)).map_err(|_| {

let body = response.bytes().await?;
Ok(bld.body(Body::from(body)).map_err(|_| {
AppError::AssertionFailed("should not fail to construct response")
})?)
}
Expand Down Expand Up @@ -467,7 +517,7 @@ async fn route_request(
}

/// HTTP/1.1 hop-by-hop headers
mod hop_by_hop {
mod known_headers {
use reqwest::header::HeaderName;
pub(super) static HOP_BY_HOP: [HeaderName; 8] = [
HeaderName::from_static("keep-alive"),
Expand All @@ -479,5 +529,7 @@ mod hop_by_hop {
HeaderName::from_static("proxy-authorization"),
HeaderName::from_static("proxy-authenticate"),
];
pub(super) static BLACKLISTED: [HeaderName; 1] = [HeaderName::from_static("x-script-name")];
}
use hop_by_hop::HOP_BY_HOP;
use known_headers::BLACKLISTED;
use known_headers::HOP_BY_HOP;

0 comments on commit 1031478

Please sign in to comment.