Skip to content
Merged
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
5 changes: 5 additions & 0 deletions Cargo.lock

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

4 changes: 2 additions & 2 deletions libdd-common-ffi/src/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ mod tests {
len: 0,
_marker: PhantomData,
};
assert_eq!(null_len0.as_slice(), &[]);
assert_eq!(null_len0.as_slice(), &[] as &[u8]);
Comment thread
danielsn marked this conversation as resolved.
}

#[should_panic]
Expand Down Expand Up @@ -447,7 +447,7 @@ mod tests {
};

let result = null_zero_len.try_as_slice();
assert_eq!(result.unwrap(), &[]);
assert_eq!(result.unwrap(), &[] as &[u8]);
}

#[test]
Expand Down
4 changes: 2 additions & 2 deletions libdd-common-ffi/src/slice_mut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ mod tests {
len: 0,
_marker: PhantomData,
};
assert_eq!(null_len0.as_mut_slice(), &[]);
assert_eq!(null_len0.as_mut_slice(), &[] as &[u8]);
}

#[should_panic]
Expand Down Expand Up @@ -281,7 +281,7 @@ mod tests {
};

let result = null_zero_len.try_as_slice();
assert_eq!(result.unwrap(), &[]);
assert_eq!(result.unwrap(), &[] as &[u8]);
}

#[test]
Expand Down
16 changes: 15 additions & 1 deletion libdd-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,23 @@ futures = "0.3"
futures-core = { version = "0.3.0", default-features = false }
futures-util = { version = "0.3.0", default-features = false }
hex = "0.4"
httparse = { version = "1.9", optional = true }
hyper = { workspace = true }
hyper-util = { workspace = true }
http = "1.0"
http-body = "1.0"
http-body-util = "0.1"
tower-service = "0.3"
cc = "1.1.31"
mime = { version = "0.3.16", optional = true }
multipart = { version = "0.18", optional = true }
pin-project = "1"
rand = { version = "0.8", optional = true }
regex = "1.5"
reqwest = { version = "0.13", features = ["rustls"], default-features = false, optional = true }
rustls-native-certs = { version = "0.8.1", optional = true }
thiserror = "1.0"
tokio = { version = "1.23", features = ["rt", "macros"] }
tokio = { version = "1.23", features = ["rt", "macros", "net", "io-util", "fs"] }
tokio-rustls = { version = "0.26", default-features = false, optional = true }
serde = { version = "1.0", features = ["derive"] }
static_assertions = "1.1.0"
Expand Down Expand Up @@ -62,9 +67,14 @@ hyper-rustls = { version = "0.27", default-features = false, features = [
], optional = true }

[dev-dependencies]
httparse = "1.9"
indexmap = "2.11"
maplit = "1.0"
mime = "0.3.16"
multipart = "0.18"
rand = "0.8"
tempfile = "3.8"
tokio = { version = "1.23", features = ["rt", "macros", "time"] }

[features]
default = ["https"]
Expand All @@ -75,6 +85,10 @@ use_webpki_roots = ["hyper-rustls/webpki-roots"]
cgroup_testing = []
# FIPS mode uses the FIPS-compliant cryptographic provider (Unix only)
fips = ["https", "hyper-rustls/fips"]
# Enable reqwest client builder support with file dump debugging
reqwest = ["dep:reqwest", "test-utils"]
# Enable test utilities for use in other crates
test-utils = ["dep:httparse", "dep:rand", "dep:mime", "dep:multipart"]

[lints.rust]
# We run coverage checks in our github actions. These checks are run with
Expand Down
3 changes: 2 additions & 1 deletion libdd-common/src/cc_utils.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright 2021-Present Datadog, Inc. https://www.datadoghq.com/
// SPDX-License-Identifier: Apache-2.0

use anyhow::Context;
use std::{
env,
ffi::{self, OsString},
Expand Down Expand Up @@ -122,7 +123,7 @@ impl ImprovedBuild {
fn get_out_dir(&self) -> anyhow::Result<PathBuf> {
env::var_os("OUT_DIR")
.map(PathBuf::from)
.ok_or_else(|| anyhow::Error::msg("can't get output directory info"))
.context("can't get output directory info")
}

// cc::Build shadow
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const HTTP_200_RESPONSE: &[u8] = b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"
/// # Returns
/// The path to the Unix socket (on Unix) or named pipe (on Windows) that the server is listening on
#[cfg(unix)]
pub(crate) fn spawn_dump_server(output_path: PathBuf) -> anyhow::Result<PathBuf> {
pub fn spawn_dump_server(output_path: PathBuf) -> anyhow::Result<PathBuf> {
use tokio::net::UnixListener;

// Create a temporary socket path with randomness to avoid collisions
Expand Down Expand Up @@ -86,7 +86,7 @@ pub(crate) fn spawn_dump_server(output_path: PathBuf) -> anyhow::Result<PathBuf>
/// # Returns
/// The path to the Windows named pipe that the server is listening on
#[cfg(windows)]
pub(crate) fn spawn_dump_server(output_path: PathBuf) -> anyhow::Result<PathBuf> {
pub fn spawn_dump_server(output_path: PathBuf) -> anyhow::Result<PathBuf> {
use tokio::net::windows::named_pipe::ServerOptions;

// Create a unique named pipe name with randomness to avoid collisions
Expand Down
90 changes: 81 additions & 9 deletions libdd-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@
#![cfg_attr(not(test), deny(clippy::todo))]
#![cfg_attr(not(test), deny(clippy::unimplemented))]

use hyper::{
header::HeaderValue,
http::uri::{self},
};
use anyhow::Context;
use hyper::{header::HeaderValue, http::uri};
use serde::de::Error;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::sync::{Mutex, MutexGuard};
Expand All @@ -18,6 +16,8 @@ use std::{borrow::Cow, ops::Deref, path::PathBuf, str::FromStr};
pub mod azure_app_services;
pub mod cc_utils;
pub mod connector;
#[cfg(feature = "reqwest")]
pub mod dump_server;
pub mod entity_id;
#[macro_use]
pub mod cstr;
Expand All @@ -26,6 +26,8 @@ pub mod error;
pub mod hyper_migration;
pub mod rate_limiter;
pub mod tag;
#[cfg(any(test, feature = "test-utils"))]
pub mod test_utils;
pub mod timeout;
pub mod unix_utils;
pub mod worker;
Expand Down Expand Up @@ -220,11 +222,7 @@ fn encode_uri_path_in_authority(scheme: &str, path: &str) -> anyhow::Result<hype
}

pub fn decode_uri_path_in_authority(uri: &hyper::Uri) -> anyhow::Result<PathBuf> {
let path = hex::decode(
uri.authority()
.ok_or_else(|| anyhow::anyhow!("missing uri authority"))?
.as_str(),
)?;
let path = hex::decode(uri.authority().context("missing uri authority")?.as_str())?;
#[cfg(unix)]
{
use std::os::unix::ffi::OsStringExt;
Expand Down Expand Up @@ -320,4 +318,78 @@ impl Endpoint {
};
self
}

/// Creates a reqwest ClientBuilder configured for this endpoint.
///
/// This method handles various endpoint schemes:
/// - `http`/`https`: Standard HTTP(S) endpoints
/// - `unix`: Unix domain sockets (Unix only)
/// - `windows`: Windows named pipes (Windows only)
/// - `file`: File dump endpoints for debugging (spawns a local server to capture requests)
///
/// # Returns
/// A tuple of (ClientBuilder, request_url) where:
/// - ClientBuilder is configured with the appropriate transport and timeout
/// - request_url is the URL string to use for HTTP requests
///
/// # Errors
/// Returns an error if:
/// - The endpoint scheme is unsupported
/// - Path decoding fails
/// - The dump server fails to start (for file:// scheme)
#[cfg(feature = "reqwest")]
pub fn to_reqwest_client_builder(&self) -> anyhow::Result<(reqwest::ClientBuilder, String)> {
use anyhow::Context;

let mut builder =
reqwest::Client::builder().timeout(std::time::Duration::from_millis(self.timeout_ms));

let request_url = match self.url.scheme_str() {
// HTTP/HTTPS endpoints
Some("http") | Some("https") => self.url.to_string(),

// File dump endpoint (debugging) - uses platform-specific local transport
Some("file") => {
let output_path = decode_uri_path_in_authority(&self.url)
.context("Failed to decode file path from URI")?;
let socket_or_pipe_path = dump_server::spawn_dump_server(output_path)?;

// Configure the client to use the local socket/pipe
#[cfg(unix)]
{
builder = builder.unix_socket(socket_or_pipe_path);
}
#[cfg(windows)]
{
builder = builder
.windows_named_pipe(socket_or_pipe_path.to_string_lossy().to_string());
}

"http://localhost/".to_string()
}

// Unix domain sockets
#[cfg(unix)]
Some("unix") => {
use connector::uds::socket_path_from_uri;
let socket_path = socket_path_from_uri(&self.url)?;
builder = builder.unix_socket(socket_path);
format!("http://localhost{}", self.url.path())
}

// Windows named pipes
#[cfg(windows)]
Some("windows") => {
use connector::named_pipe::named_pipe_path_from_uri;
let pipe_path = named_pipe_path_from_uri(&self.url)?;
builder = builder.windows_named_pipe(pipe_path.to_string_lossy().to_string());
format!("http://localhost{}", self.url.path())
}

// Unsupported schemes
scheme => anyhow::bail!("Unsupported endpoint scheme: {:?}", scheme),
};

Ok((builder, request_url))
}
}
Loading
Loading