Skip to content

Commit f0bb501

Browse files
Fix TLS not enabling for dynamic fastly backends (#35)
1 parent 1b1dee5 commit f0bb501

1 file changed

Lines changed: 51 additions & 30 deletions

File tree

  • crates/edgezero-adapter-fastly/src

crates/edgezero-adapter-fastly/src/proxy.rs

Lines changed: 51 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ use edgezero_core::error::EdgeError;
77
use edgezero_core::http::{header, HeaderMap, HeaderValue, Method, Uri};
88
use edgezero_core::proxy::{ProxyClient, ProxyRequest, ProxyResponse};
99
use fastly::{
10-
http::body::StreamingBody, Backend, Request as FastlyRequest, Response as FastlyResponse,
10+
error::anyhow, http::body::StreamingBody, Backend, Request as FastlyRequest,
11+
Response as FastlyResponse,
1112
};
1213
use futures_util::stream::{BoxStream, StreamExt};
13-
use std::collections::hash_map::DefaultHasher;
14-
use std::hash::{Hash, Hasher};
1514
use std::io::{self, Write};
15+
use std::time::Duration;
1616

1717
const BACKEND_PREFIX: &str = "edgezero-dynamic-";
1818

@@ -22,10 +22,10 @@ pub struct FastlyProxyClient;
2222
impl ProxyClient for FastlyProxyClient {
2323
async fn send(&self, request: ProxyRequest) -> Result<ProxyResponse, EdgeError> {
2424
let (method, uri, headers, body, _ext) = request.into_parts();
25-
let backend = ensure_backend(&uri)?;
25+
let backend_name = ensure_backend(&uri)?;
2626
let fastly_request = build_fastly_request(method, &uri, headers)?;
2727
let (mut streaming_body, pending_request) = fastly_request
28-
.send_async_streaming(backend.name())
28+
.send_async_streaming(&backend_name)
2929
.map_err(EdgeError::internal)?;
3030
forward_request_body(body, &mut streaming_body).await?;
3131
streaming_body.finish().map_err(EdgeError::internal)?;
@@ -88,45 +88,66 @@ async fn forward_request_body(
8888
Ok(())
8989
}
9090

91-
fn ensure_backend(uri: &Uri) -> Result<Backend, EdgeError> {
91+
fn ensure_backend(uri: &Uri) -> Result<String, EdgeError> {
9292
let host = uri
9393
.host()
9494
.ok_or_else(|| EdgeError::bad_request("proxy target must include host"))?;
95-
let port = uri.port_u16();
96-
let target = match port {
97-
Some(port) => format!("{}:{}", host, port),
98-
None => host.to_string(),
99-
};
10095

101-
let name = backend_name(&target, uri.scheme_str());
96+
let scheme = uri.scheme_str().unwrap_or("https");
97+
let is_https = scheme.eq_ignore_ascii_case("https");
10298

103-
let host_with_port = match port {
104-
Some(p) => format!("{}:{}", host, p),
105-
None => host.to_string(),
99+
let target_port = match (uri.port_u16(), is_https) {
100+
(Some(p), _) => p,
101+
(None, true) => 443,
102+
(None, false) => 80,
106103
};
107104

108-
let builder = Backend::builder(&name, &host_with_port).override_host(host);
105+
let host_with_port = format!("{}:{}", host, target_port);
106+
107+
// Human-readable name: backend_{scheme}_{host}_{port} with dots/colons sanitised
108+
let name_base = format!("{}_{}_{}", scheme, host, target_port);
109+
let backend_name = format!("{}{}", BACKEND_PREFIX, name_base.replace(['.', ':'], "_"));
110+
111+
let mut builder = Backend::builder(&backend_name, &host_with_port)
112+
.override_host(host)
113+
.connect_timeout(Duration::from_secs(1))
114+
.first_byte_timeout(Duration::from_secs(15))
115+
.between_bytes_timeout(Duration::from_secs(10));
116+
117+
if is_https {
118+
builder = builder
119+
.enable_ssl()
120+
.sni_hostname(host)
121+
.check_certificate(host);
122+
log::debug!("enable ssl for backend: {}", backend_name);
123+
}
109124

110125
match builder.finish() {
111-
Ok(backend) => Ok(backend),
112-
Err(_) => {
113-
let mut builder = Backend::builder(&name, &target);
114-
if uri.scheme_str() == Some("https") {
115-
builder = builder.enable_ssl();
126+
Ok(_) => {
127+
log::debug!(
128+
"created dynamic backend: {} -> {}",
129+
backend_name,
130+
host_with_port
131+
);
132+
Ok(backend_name)
133+
}
134+
Err(e) => {
135+
let msg = e.to_string();
136+
if msg.contains("NameInUse") || msg.contains("already in use") {
137+
log::debug!("reusing existing dynamic backend: {}", backend_name);
138+
Ok(backend_name)
139+
} else {
140+
Err(EdgeError::internal(anyhow!(
141+
"dynamic backend creation failed ({} -> {}): {}",
142+
backend_name,
143+
host_with_port,
144+
msg
145+
)))
116146
}
117-
builder.finish().map_err(EdgeError::internal)
118147
}
119148
}
120149
}
121150

122-
fn backend_name(target: &str, scheme: Option<&str>) -> String {
123-
let mut hasher = DefaultHasher::new();
124-
target.hash(&mut hasher);
125-
scheme.hash(&mut hasher);
126-
let hash = hasher.finish();
127-
format!("{}{:016x}", BACKEND_PREFIX, hash)
128-
}
129-
130151
fn convert_response(fastly_response: &mut FastlyResponse) -> Result<ProxyResponse, EdgeError> {
131152
let status = fastly_response.get_status();
132153
let mut proxy_response = ProxyResponse::new(status, Body::empty());

0 commit comments

Comments
 (0)