Skip to content
Draft
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
910 changes: 626 additions & 284 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,14 @@ dynamic-linking = ["rdkafka-sys/dynamic-linking"]
dynamic_linking = ["rdkafka-sys/dynamic_linking"]
ssl = ["rdkafka-sys/ssl"]
ssl-vendored = ["rdkafka-sys/ssl-vendored"]
aws-lc = ["rdkafka-sys/aws-lc"]
gssapi = ["rdkafka-sys/gssapi"]
gssapi-vendored = ["rdkafka-sys/gssapi-vendored"]
sasl = ["rdkafka-sys/sasl"]
libz = ["rdkafka-sys/libz"]
libz-static = ["rdkafka-sys/libz-static"]
curl = ["rdkafka-sys/curl"]
curl-rustls = ["rdkafka-sys/curl-rustls"]
curl-static = ["rdkafka-sys/curl-static"]
zstd = ["rdkafka-sys/zstd"]
zstd-pkg-config = ["rdkafka-sys/zstd-pkg-config"]
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,11 @@ your executable. To compile librdkafka you'll need:
Note that using the CMake build system, via the `cmake-build` feature, is
encouraged if you can take the dependency on CMake.

OpenSSL remains the backend selected by the existing `ssl` and
`ssl-vendored` features. Alternatively, enable `aws-lc` to compile librdkafka
against the AWS-LC build provided by `aws-lc-sys`. The `ssl` and `aws-lc` TLS
backend choices are mutually exclusive.

By default a submodule with the librdkafka sources pinned to a specific
commit will be used to compile and statically link the library. The
`dynamic-linking` feature can be used to instead dynamically link rdkafka to
Expand Down
15 changes: 11 additions & 4 deletions rdkafka-sys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ rust-version = "1.74"
num_enum = "0.7.3"
libc = "0.2.172"
openssl-sys = { version = "0.9.108", optional = true }
aws-lc-sys = { version = "0.43.0", git = "https://github.com/justsmth/aws-lc-rs.git", rev = "a06191c2e203b39065dc5ce99ae244adf464cc5f", optional = true, features = ["ssl"] }
libz-sys = { version = "1.1.22", optional = true }
curl-sys = { version = "0.4.80", optional = true }
curl-sys = { version = "0.4.80", optional = true, default-features = false }
zstd-sys = { version = "2.0.15", optional = true }
lz4-sys = { version = "1.11.1", optional = true }
sasl2-sys = { version = "0.1.22", optional = true }
Expand Down Expand Up @@ -55,6 +56,9 @@ ssl = ["openssl-sys"]
# crate.
ssl-vendored = ["ssl", "openssl-sys/vendored"]

# Enable SSL support using the AWS-LC build provided by aws-lc-sys.
aws-lc = ["dep:aws-lc-sys"]

# Enable SASL GSSAPI support with Cyrus libsasl2.
gssapi = ["ssl", "sasl2-sys"]

Expand All @@ -71,12 +75,15 @@ libz = ["libz-sys"]
# the system's version.
libz-static = ["libz", "libz-sys/static"]

# Enable support for HTTP client via curl.
curl = ["curl-sys"]
# Enable support for HTTP client via curl using OpenSSL.
curl = ["dep:curl-sys", "curl-sys/ssl"]

# Enable support for HTTP client via curl using Rustls.
curl-rustls = ["dep:curl-sys", "curl-sys/rustls"]

# Link against the version of curl bundled with the curl-sys crate, rather than
# the system's version.
curl-static = ["curl-sys/static-curl"]
curl-static = ["dep:curl-sys", "curl-sys/static-curl"]

# Enable support for zstd compression.
zstd = ["zstd-sys"]
Expand Down
4 changes: 4 additions & 0 deletions rdkafka-sys/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ flags you would pass to `configure` if you were compiling manually).
OpenSSL library is dynamically linked, but static linking of the version
bundled with the [openssl-sys] crate can be requested with the
`ssl-vendored` feature.
* The **`aws-lc`** feature enables SSL support using the AWS-LC source build
provided by [aws-lc-sys]. The `ssl` and `aws-lc` TLS backend features are
mutually exclusive.
* The **`gssapi`** feature enables SASL GSSAPI support with Cyrus
libsasl2. By default the system's libsasl2 is dynamically linked, but
static linking of the version bundled with the [sasl2-sys] crate can be
Expand Down Expand Up @@ -88,6 +91,7 @@ process is defined in [`build.rs`].

[`build.rs`]: https://github.com/fede1024/rust-rdkafka/tree/master/rdkafka-sys/build.rs
[Apache Kafka]: https://kafka.apache.org
[aws-lc-sys]: https://crates.io/crates/aws-lc-sys
[CMake]: https://cmake.org
[libz-sys]: https://crates.io/crates/libz-sys
[curl-sys]: https://crates.io/crates/curl-sys
Expand Down
177 changes: 166 additions & 11 deletions rdkafka-sys/build.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::borrow::Borrow;
use std::env;
use std::ffi::OsStr;
#[cfg(feature = "cmake-build")]
use std::fs;
use std::path::{Path, PathBuf};
use std::process::{self, Command};
Expand Down Expand Up @@ -40,6 +39,16 @@ where
}

fn main() {
let ssl_backend = SslBackend::selected();
if ssl_backend.is_some_and(SslBackend::is_aws_lc)
&& env::var("CARGO_FEATURE_DYNAMIC_LINKING").is_ok()
{
panic!(
"the `aws-lc` feature only applies when building bundled \
librdkafka; it cannot be combined with `dynamic-linking`"
);
}

if env::var("CARGO_FEATURE_DYNAMIC_LINKING").is_ok() {
eprintln!("librdkafka will be linked dynamically");

Expand Down Expand Up @@ -96,16 +105,142 @@ fn main() {
run_command_or_fail("../", "git", &["submodule", "update", "--init"]);
}
eprintln!("Building and linking librdkafka statically");
build_librdkafka();
build_librdkafka(ssl_backend);
}
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum SslBackend {
OpenSsl,
AwsLc,
}

impl SslBackend {
fn selected() -> Option<Self> {
let enabled = [
("CARGO_FEATURE_SSL", Self::OpenSsl),
("CARGO_FEATURE_AWS_LC", Self::AwsLc),
]
.into_iter()
.filter_map(|(feature, backend)| env::var_os(feature).map(|_| backend))
.collect::<Vec<_>>();

assert!(
enabled.len() <= 1,
"the `ssl` and `aws-lc` features select different TLS backends \
and are mutually exclusive"
);
enabled.into_iter().next()
}

fn is_aws_lc(self) -> bool {
matches!(self, Self::AwsLc)
}
}

struct AwsLc {
include: PathBuf,
libcrypto: PathBuf,
libssl: PathBuf,
link_kind: String,
}

impl AwsLc {
fn from_env(backend: SslBackend) -> Self {
assert!(backend.is_aws_lc());
let metadata_prefix = match backend {
SslBackend::AwsLc => "DEP_AWS_LC_",
SslBackend::OpenSsl => unreachable!(),
};
let include_var = env::vars_os()
.map(|(name, _)| name.to_string_lossy().into_owned())
.find(|name| name.starts_with(metadata_prefix) && name.ends_with("_INCLUDE"))
.unwrap_or_else(|| panic!("AWS-LC sys crate did not export its include directory"));
let prefix = include_var
.strip_suffix("INCLUDE")
.expect("AWS-LC include metadata has an invalid name");
let get = |suffix: &str| {
env::var_os(format!("{prefix}{suffix}"))
.unwrap_or_else(|| panic!("AWS-LC sys crate did not export {suffix}"))
};

let result = Self {
include: PathBuf::from(get("INCLUDE")),
libcrypto: PathBuf::from(get("LIBCRYPTO_PATH")),
libssl: PathBuf::from(get("LIBSSL_PATH")),
link_kind: get("LINK_KIND").to_string_lossy().into_owned(),
};
assert!(
result.include.is_dir(),
"AWS-LC include directory does not exist: {}",
result.include.display()
);
assert!(
result.libcrypto.is_file(),
"AWS-LC libcrypto does not exist: {}",
result.libcrypto.display()
);
assert!(
result.libssl.is_file(),
"AWS-LC libssl does not exist: {}",
result.libssl.display()
);
assert!(
matches!(result.link_kind.as_str(), "static" | "dylib"),
"AWS-LC sys crate exported unsupported link kind: {}",
result.link_kind
);
result
}

#[cfg(not(feature = "cmake-build"))]
fn stage_openssl_library_names(&self) -> PathBuf {
let stage = PathBuf::from(env::var_os("OUT_DIR").expect("OUT_DIR missing"))
.join("aws-lc-openssl-names");
fs::create_dir_all(&stage).expect("failed to create AWS-LC staging directory");
self.stage_library(&self.libcrypto, &stage, "crypto");
self.stage_library(&self.libssl, &stage, "ssl");
stage
}

#[cfg(not(feature = "cmake-build"))]
fn stage_library(&self, source: &Path, stage: &Path, openssl_name: &str) {
let source_name = source
.file_name()
.and_then(OsStr::to_str)
.expect("AWS-LC library filename is not valid UTF-8");
let staged_name = if source_name.ends_with(".dll.a") {
format!("lib{openssl_name}.dll.a")
} else if let Some(index) = source_name.find(".so") {
format!("lib{openssl_name}{}", &source_name[index..])
} else {
match source.extension().and_then(OsStr::to_str) {
Some("a") => format!("lib{openssl_name}.a"),
Some("dylib") => format!("lib{openssl_name}.dylib"),
Some("lib") => format!("{openssl_name}.lib"),
extension => panic!(
"unsupported AWS-LC library extension {extension:?}: {}",
source.display()
),
}
};
fs::copy(source, stage.join(staged_name)).unwrap_or_else(|error| {
panic!(
"failed to stage AWS-LC library {}: {error}",
source.display()
)
});
}
}

fn needs_curl() -> bool {
env::var("CARGO_FEATURE_CURL").is_ok() || env::var("CARGO_FEATURE_CURL_STATIC").is_ok()
env::var("CARGO_FEATURE_CURL").is_ok()
|| env::var("CARGO_FEATURE_CURL_RUSTLS").is_ok()
|| env::var("CARGO_FEATURE_CURL_STATIC").is_ok()
}

#[cfg(not(feature = "cmake-build"))]
fn build_librdkafka() {
fn build_librdkafka(ssl_backend: Option<SslBackend>) {
let mut configure_flags: Vec<String> = Vec::new();

let mut cflags = Vec::new();
Expand All @@ -118,11 +253,16 @@ fn build_librdkafka() {
ldflags.push(var);
}

if env::var("CARGO_FEATURE_SSL").is_ok() {
if let Some(backend) = ssl_backend {
configure_flags.push("--enable-ssl".into());
if let Ok(openssl_root) = env::var("DEP_OPENSSL_ROOT") {
cflags.push(format!("-I{}/include", openssl_root));
ldflags.push(format!("-L{}/lib", openssl_root));
if backend.is_aws_lc() {
let aws_lc = AwsLc::from_env(backend);
let lib_dir = aws_lc.stage_openssl_library_names();
cflags.push(format!("-I{}", aws_lc.include.display()));
ldflags.push(format!("-L{}", lib_dir.display()));
} else if let Ok(openssl_root) = env::var("DEP_OPENSSL_ROOT") {
cflags.push(format!("-I{openssl_root}/include"));
ldflags.push(format!("-L{openssl_root}/lib"));
}
} else {
configure_flags.push("--disable-ssl".into());
Expand Down Expand Up @@ -217,7 +357,7 @@ fn build_librdkafka() {
}

#[cfg(feature = "cmake-build")]
fn build_librdkafka() {
fn build_librdkafka(ssl_backend: Option<SslBackend>) {
let mut config = cmake::Config::new("librdkafka");
let mut cmake_library_paths = vec![];

Expand Down Expand Up @@ -268,11 +408,26 @@ fn build_librdkafka() {
config.define("WITH_CURL", "0");
}

if env::var("CARGO_FEATURE_SSL").is_ok() {
if let Some(backend) = ssl_backend {
config.define("WITH_SSL", "1");
config.define("WITH_SASL_SCRAM", "1");
config.define("WITH_SASL_OAUTHBEARER", "1");
config.register_dep("openssl");
if backend.is_aws_lc() {
let aws_lc = AwsLc::from_env(backend);
config.define(
"OPENSSL_USE_STATIC_LIBS",
if aws_lc.link_kind == "static" {
"1"
} else {
"0"
},
);
config.define("OPENSSL_INCLUDE_DIR", &aws_lc.include);
config.define("OPENSSL_SSL_LIBRARY", &aws_lc.libssl);
config.define("OPENSSL_CRYPTO_LIBRARY", &aws_lc.libcrypto);
} else {
config.register_dep("openssl");
}
} else {
config.define("WITH_SSL", "0");
}
Expand Down
13 changes: 12 additions & 1 deletion rdkafka-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@
//! OpenSSL library is dynamically linked, but static linking of the version
//! bundled with the [openssl-sys] crate can be requested with the
//! `ssl-vendored` feature.
//! * The **`aws-lc`** feature enables SSL support using the AWS-LC source
//! build provided by [aws-lc-sys]. The `ssl` and `aws-lc` TLS backend
//! features are mutually exclusive.
//! * The **`gssapi`** feature enables SASL GSSAPI support with Cyrus
//! libsasl2. By default the system's libsasl2 is dynamically linked, but
//! static linking of the version bundled with the [sasl2-sys] crate can be
Expand Down Expand Up @@ -81,6 +84,7 @@
//!
//! [`build.rs`]: https://github.com/fede1024/rust-rdkafka/tree/master/rdkafka-sys/build.rs
//! [Apache Kafka]: https://kafka.apache.org
//! [aws-lc-sys]: https://crates.io/crates/aws-lc-sys
//! [CMake]: https://cmake.org
//! [libz-sys]: https://crates.io/crates/libz-sys
//! [curl-sys]: https://crates.io/crates/curl-sys
Expand All @@ -94,13 +98,20 @@
#[cfg(feature = "openssl-sys")]
extern crate openssl_sys;

#[cfg(feature = "aws-lc")]
extern crate aws_lc_sys;

#[cfg(feature = "sasl2-sys")]
extern crate sasl2_sys;

#[cfg(feature = "libz-sys")]
extern crate libz_sys;

#[cfg(any(feature = "curl-sys", feature = "curl-static"))]
#[cfg(any(
feature = "curl",
feature = "curl-rustls",
feature = "curl-static"
))]
extern crate curl_sys;

#[cfg(feature = "zstd-sys")]
Expand Down
9 changes: 9 additions & 0 deletions rdkafka-sys/tests/aws_lc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#[cfg(feature = "aws-lc")]
#[test]
fn initializes_aws_lc_ssl() {
unsafe {
let context = aws_lc_sys::SSL_CTX_new(aws_lc_sys::TLS_method());
assert!(!context.is_null());
aws_lc_sys::SSL_CTX_free(context);
}
}
13 changes: 13 additions & 0 deletions tests/ssl_backends.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#[cfg(feature = "aws-lc")]
#[test]
fn initializes_aws_lc_backed_client() {
use rdkafka::config::ClientConfig;
use rdkafka::producer::BaseProducer;

let producer: BaseProducer = ClientConfig::new()
.set("bootstrap.servers", "localhost:1")
.set("security.protocol", "ssl")
.create()
.expect("AWS-LC-backed SSL initialization should succeed");
drop(producer);
}