Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions bottlecap/src/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use tracing::{debug, error, warn};

#[must_use]
pub fn get_client(config: &Arc<config::Config>) -> reqwest::Client {
build_client(config).unwrap_or_else(|e| {
build_client(config, true).unwrap_or_else(|e| {
error!(
"Unable to parse proxy configuration: {}, no proxy will be used",
e
Expand All @@ -23,7 +23,28 @@ pub fn get_client(config: &Arc<config::Config>) -> reqwest::Client {
})
}

fn build_client(config: &Arc<config::Config>) -> Result<reqwest::Client, Box<dyn Error>> {
/// Same as [`get_client`], but never follows redirects. reqwest keeps custom
/// headers such as `DD-API-KEY` when it follows a redirect to another host.
/// Returns `None` rather than fall back to a client that follows redirects.
#[must_use]
pub fn get_client_without_redirects(config: &Arc<config::Config>) -> Option<reqwest::Client> {
build_client(config, false)
.or_else(|e| {
error!("Unable to parse proxy configuration: {e}, no proxy will be used");
Ok::<_, Box<dyn Error>>(
create_reqwest_client_builder()?
.redirect(reqwest::redirect::Policy::none())
.build()?,
)
})
.inspect_err(|e| error!("Unable to build an HTTP client without redirects: {e}"))
.ok()
}

fn build_client(
config: &Arc<config::Config>,
follow_redirects: bool,
) -> Result<reqwest::Client, Box<dyn Error>> {
let mut client = create_reqwest_client_builder()?
.timeout(Duration::from_secs(config.flush_timeout))
// Disable connection pooling to avoid stale connections after Lambda freeze/resume.
Expand All @@ -33,6 +54,9 @@ fn build_client(config: &Arc<config::Config>) -> Result<reqwest::Client, Box<dyn
// pattern in `traces/http_client.rs` and libdatadog's `new_client_periodic`.
.pool_max_idle_per_host(0)
.danger_accept_invalid_certs(config.skip_ssl_validation);
if !follow_redirects {
client = client.redirect(reqwest::redirect::Policy::none());
}

// Determine if we should use HTTP/1 or HTTP/2
let should_use_http1 = match &config.http_protocol {
Expand Down
12 changes: 10 additions & 2 deletions bottlecap/src/startup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ use crate::config::Config;
use crate::lifecycle::invocation::processor_service::InvocationProcessorHandle;
use crate::tags::provider::Provider as TagProvider;
use crate::traces::{
http_client as trace_http_client, proxy_aggregator,
http_client as trace_http_client,
passthrough::Passthrough,
proxy_aggregator,
proxy_flusher::Flusher as ProxyFlusher,
span_dedup_service,
stats_aggregator::StatsAggregator,
Expand Down Expand Up @@ -195,7 +197,7 @@ pub fn build_trace_agent(
client.clone(),
));

let trace_agent = trace_agent::TraceAgent::new(
let mut trace_agent = trace_agent::TraceAgent::new(
Arc::clone(config),
trace_aggregator_handle.clone(),
trace_processor.clone(),
Expand All @@ -208,6 +210,12 @@ pub fn build_trace_agent(
stats_concentrator_handle.clone(),
span_dedup_handle,
);
// Without a client that refuses redirects, leave the route out (404)
// rather than risk sending the API key elsewhere.
if let Some(client) = crate::http::get_client_without_redirects(config) {
trace_agent =
trace_agent.with_passthrough(Passthrough::new(config, api_key_factory.clone(), client));
}
let pipeline = TraceAgentPipeline {
trace_tx: trace_agent.get_sender_copy(),
trace_flusher,
Expand Down
1 change: 1 addition & 0 deletions bottlecap/src/traces/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

pub mod data_streams;
pub mod http_client;
pub mod passthrough;
pub mod propagation;
pub mod proxy_aggregator;
pub mod proxy_flusher;
Expand Down
Loading
Loading