diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 0568641..a696478 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -34,6 +34,8 @@ jobs: steps: - name: checkout source uses: actions/checkout@v4 + with: + submodules: true - name: set up cargo cache uses: actions/cache@v4 continue-on-error: false @@ -89,6 +91,8 @@ jobs: steps: - name: checkout source uses: actions/checkout@v4 + with: + submodules: true - name: set up cargo cache uses: actions/cache@v4 continue-on-error: false @@ -123,6 +127,8 @@ jobs: - name: install command line dependencies run: brew install make gnupg - uses: actions/checkout@v4 + with: + submodules: true - uses: dtolnay/rust-toolchain@stable - name: set up cargo cache uses: actions/cache@v4 diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..d4fe729 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "nginx-src/nginx"] + path = nginx-src/nginx + url = https://github.com/nginx/nginx.git + branch = stable-1.28 diff --git a/Cargo.lock b/Cargo.lock index ec61a41..2b9988a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -87,12 +87,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - [[package]] name = "bindgen" version = "0.71.1" @@ -204,15 +198,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crc32fast" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" -dependencies = [ - "cfg-if", -] - [[package]] name = "crypto-common" version = "0.1.6" @@ -257,12 +242,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" -[[package]] -name = "env_home" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" - [[package]] name = "errno" version = "0.3.12" @@ -288,26 +267,10 @@ dependencies = [ ] [[package]] -name = "filetime" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" -dependencies = [ - "cfg-if", - "libc", - "libredox", - "windows-sys 0.59.0", -] - -[[package]] -name = "flate2" -version = "1.1.1" +name = "fastrand" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" -dependencies = [ - "crc32fast", - "miniz_oxide", -] +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fnv" @@ -374,12 +337,6 @@ dependencies = [ "itoa", ] -[[package]] -name = "httparse" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" - [[package]] name = "iana-time-zone" version = "0.1.63" @@ -476,17 +433,6 @@ dependencies = [ "windows-targets 0.53.0", ] -[[package]] -name = "libredox" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" -dependencies = [ - "bitflags", - "libc", - "redox_syscall", -] - [[package]] name = "linux-raw-sys" version = "0.9.4" @@ -541,19 +487,22 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "nginx-src" +version = "1028.0.0" +dependencies = [ + "duct", +] + [[package]] name = "nginx-sys" version = "0.5.0" dependencies = [ "bindgen", "cc", - "duct", "dunce", - "flate2", + "nginx-src", "regex", - "tar", - "ureq", - "which", ] [[package]] @@ -561,7 +510,7 @@ name = "ngx" version = "0.5.0" dependencies = [ "nginx-sys", - "target-triple", + "tempfile", ] [[package]] @@ -748,50 +697,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "rustls" -version = "0.23.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" -dependencies = [ - "log", - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "rustls-pki-types" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" -dependencies = [ - "zeroize", -] - -[[package]] -name = "rustls-webpki" -version = "0.103.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - [[package]] name = "rustversion" version = "1.0.21" @@ -875,12 +780,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - [[package]] name = "syn" version = "2.0.101" @@ -893,22 +792,17 @@ dependencies = [ ] [[package]] -name = "tar" -version = "0.4.44" +name = "tempfile" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ - "filetime", - "libc", - "xattr", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", ] -[[package]] -name = "target-triple" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac9aa371f599d22256307c24a9d748c041e548cbf599f35d890f9d365361790" - [[package]] name = "tinyvec" version = "1.9.0" @@ -992,36 +886,6 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" -[[package]] -name = "ureq" -version = "3.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7a3e9af6113ecd57b8c63d3cd76a385b2e3881365f1f489e54f49801d0c83ea" -dependencies = [ - "base64", - "flate2", - "log", - "percent-encoding", - "rustls", - "rustls-pemfile", - "rustls-pki-types", - "ureq-proto", - "utf-8", - "webpki-roots 0.26.11", -] - -[[package]] -name = "ureq-proto" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fadf18427d33828c311234884b7ba2afb57143e6e7e69fda7ee883b624661e36" -dependencies = [ - "base64", - "http", - "httparse", - "log", -] - [[package]] name = "url" version = "2.5.4" @@ -1033,12 +897,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -1115,36 +973,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "webpki-roots" -version = "0.26.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" -dependencies = [ - "webpki-roots 1.0.0", -] - -[[package]] -name = "webpki-roots" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "which" -version = "7.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762" -dependencies = [ - "either", - "env_home", - "rustix", - "winsafe", -] - [[package]] name = "windows-core" version = "0.61.2" @@ -1349,25 +1177,3 @@ name = "windows_x86_64_msvc" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" - -[[package]] -name = "winsafe" -version = "0.0.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" - -[[package]] -name = "xattr" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e" -dependencies = [ - "libc", - "rustix", -] - -[[package]] -name = "zeroize" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/Cargo.toml b/Cargo.toml index d46cb3d..e9718a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ + "nginx-src", "nginx-sys", "examples", ] @@ -45,4 +46,4 @@ vendored = ["nginx-sys/vendored"] maintenance = { status = "experimental" } [dev-dependencies] -target-triple = "0.1.2" +tempfile = { version = "3.20.0", default-features = false } diff --git a/build.rs b/build.rs index 1f8c574..51fc45d 100644 --- a/build.rs +++ b/build.rs @@ -53,6 +53,12 @@ fn main() { } } + // Pass build directory to the tests + println!("cargo::rerun-if-env-changed=DEP_NGINX_BUILD_DIR"); + if let Ok(build_dir) = std::env::var("DEP_NGINX_BUILD_DIR") { + println!("cargo::rustc-env=DEP_NGINX_BUILD_DIR={}", build_dir); + } + // Generate required compiler flags if cfg!(target_os = "macos") { // https://stackoverflow.com/questions/28124221/error-linking-with-cc-failed-exit-code-1 diff --git a/deny.toml b/deny.toml index 39f40a6..71ff852 100644 --- a/deny.toml +++ b/deny.toml @@ -6,8 +6,8 @@ all-features = true [licenses] allow = [ - "Apache-2.0 WITH LLVM-exception", "Apache-2.0", + "BSD-2-Clause", "BSD-3-Clause", "ISC", "MIT", @@ -15,10 +15,6 @@ allow = [ ] confidence-threshold = 0.8 -[[licenses.exceptions]] -crate = "webpki-roots" -allow = ["CDLA-Permissive-2.0", "MPL-2.0"] - [[licenses.clarify]] crate = "ring" expression = "MIT AND ISC AND OpenSSL" diff --git a/nginx-src/Cargo.toml b/nginx-src/Cargo.toml new file mode 100644 index 0000000..dd2204a --- /dev/null +++ b/nginx-src/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "nginx-src" +version = "1028.0.0" +license = "BSD-2-Clause" +description = "Source of NGINX" +keywords = ["nginx", "module", "sys"] +edition.workspace = true +homepage.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +duct = "1" diff --git a/nginx-src/LICENSE b/nginx-src/LICENSE new file mode 100644 index 0000000..4efd90b --- /dev/null +++ b/nginx-src/LICENSE @@ -0,0 +1,24 @@ +Copyright (C) 2002-2021 Igor Sysoev +Copyright (C) 2011-2025 Nginx, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff --git a/nginx-src/README.md b/nginx-src/README.md new file mode 100644 index 0000000..13f55c5 --- /dev/null +++ b/nginx-src/README.md @@ -0,0 +1,40 @@ +# nginx-src + +This crate contains a vendored copy of the NGINX source and the logic to +build it. It is intended to be consumed by the `nginx-sys` crate for CI +builds, tests or rustdoc generation. + +It is notably not intended for producing binaries suitable for production +use. For such scenaros we recommend building the ngx-rust based module +against prebuilt packages from https://nginx.org/ or your preferred +distribution. See the `nginx-sys` documentation for building ngx-rust +modules against an existing pre-configured NGINX source tree. + +## Versioning + +This crate follows the latest stable branch of NGINX. + + * The major version is derived from the major and minor version of the + NGINX stable branch being used: (`nginx.major` * 1000 + `nginx.minor`). + * The minor version is taken from the NGINX patch version. + * The patch version is incremented on changes to the build logic or crate + metadata. + +## Build Requirements + +The crate can be built on common Unix-like operating systems and requires +all the usual NGINX build dependencies (including development headers +for the libraries) installed in system paths: + +* C compiler and toolchain +* SSL library, OpenSSL or LibreSSL +* PCRE or PCRE2 +* Zlib or zlib-ng witn zlib compatibile API enabled + +We don't intend to support Windows at the moment, as NGINX does not +support dynamic modules for this target. + +## License + +This crate contains the source code of NGINX and thus inherits the +[BSD-2-Clause](nginx/LICENSE) license. diff --git a/nginx-src/nginx b/nginx-src/nginx new file mode 160000 index 0000000..481d28c --- /dev/null +++ b/nginx-src/nginx @@ -0,0 +1 @@ +Subproject commit 481d28cb4e04c8096b9b6134856891dc52ecc68f diff --git a/nginx-src/src/lib.rs b/nginx-src/src/lib.rs new file mode 100644 index 0000000..f152f58 --- /dev/null +++ b/nginx-src/src/lib.rs @@ -0,0 +1,127 @@ +use std::ffi::OsString; +use std::path::{Path, PathBuf}; +use std::{env, io, thread}; + +pub static NGINX_DEFAULT_SOURCE_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/nginx"); + +const NGINX_BUILD_INFO: &str = "last-build-info"; +const NGINX_BINARY: &str = "nginx"; + +static NGINX_CONFIGURE_FLAGS: &[&str] = &[ + "--with-compat", + "--with-http_realip_module", + "--with-http_ssl_module", + "--with-http_v2_module", + "--with-stream_realip_module", + "--with-stream_ssl_module", + "--with-stream", + "--with-threads", +]; + +pub fn build(build_dir: impl AsRef) -> io::Result<(PathBuf, PathBuf)> { + let source_dir = PathBuf::from(NGINX_DEFAULT_SOURCE_DIR); + let build_dir = build_dir.as_ref().to_owned(); + + configure(&source_dir, &build_dir, NGINX_CONFIGURE_FLAGS)?; + + make(&source_dir, &build_dir)?; + + Ok((source_dir, build_dir)) +} + +/// Run external process invoking autoconf `configure` for NGINX. +pub fn configure(source_dir: &Path, build_dir: &Path, configure_flags: &[&str]) -> io::Result<()> { + let build_info = build_info(configure_flags); + + if build_dir.join("Makefile").is_file() + && build_dir.join(NGINX_BINARY).is_file() + && matches!( + std::fs::read_to_string(build_dir.join(NGINX_BUILD_INFO)).map(|x| x == build_info), + Ok(true) + ) + { + println!("Build info unchanged, skipping configure"); + return Ok(()); + } + + println!("Using NGINX source at {:?}", source_dir); + + let configure = ["configure", "auto/configure"] + .into_iter() + .map(|x| source_dir.join(x)) + .find(|x| x.is_file()) + .ok_or(io::ErrorKind::NotFound)?; + + println!( + "Running NGINX configure script with flags: {:?}", + configure_flags.join(" ") + ); + + let mut build_dir_arg: OsString = "--builddir=".into(); + build_dir_arg.push(build_dir); + + let mut flags: Vec = configure_flags.iter().map(|x| x.into()).collect(); + flags.push(build_dir_arg); + + let output = duct::cmd(configure, flags) + .dir(source_dir) + .stderr_to_stdout() + .run()?; + + if !output.status.success() { + println!("configure failed with {:?}", output.status); + return Err(io::ErrorKind::Other.into()); + } + + let _ = std::fs::write(build_dir.join(NGINX_BUILD_INFO), build_info); + + Ok(()) +} + +/// Run `make` within the NGINX source directory as an external process. +fn make(source_dir: &Path, build_dir: &Path) -> io::Result<()> { + // Level of concurrency to use when building nginx - cargo nicely provides this information + let num_jobs = match env::var("NUM_JOBS") { + Ok(s) => s.parse::().ok(), + Err(_) => thread::available_parallelism().ok().map(|n| n.get()), + } + .unwrap_or(1); + + let run_make = |x| -> io::Result<_> { + /* Use the duct dependency here to merge the output of STDOUT and STDERR into a single stream, + and to provide the combined output as a reader which can be iterated over line-by-line. We + use duct to do this because it is a lot of work to implement this from scratch. */ + let out = duct::cmd!( + x, + "-j", + num_jobs.to_string(), + "-f", + build_dir.join("Makefile") + ) + .dir(source_dir) + .stderr_to_stdout() + .run()?; + + if !out.status.success() { + println!("{} failed with {:?}", x, out.status); + return Err(io::ErrorKind::Other.into()); + } + + Ok(()) + }; + + // Give preference to the binary with the name of gmake if it exists because this is typically + // the GNU 4+ on MacOS (if it is installed via homebrew). + match run_make("gmake") { + Ok(out) => Ok(out), + Err(err) if err.kind() == io::ErrorKind::NotFound => run_make("make"), + Err(err) => Err(err), + } +} + +/// Returns the options in which NGINX was built with +fn build_info(nginx_configure_flags: &[&str]) -> String { + // Flags should contain strings pointing to OS/platform as well as dependency versions, + // so if any of that changes, it can trigger a rebuild + nginx_configure_flags.join(" ") +} diff --git a/nginx-sys/Cargo.toml b/nginx-sys/Cargo.toml index 9188bab..fc40260 100644 --- a/nginx-sys/Cargo.toml +++ b/nginx-sys/Cargo.toml @@ -19,13 +19,9 @@ rust-version.workspace = true [build-dependencies] bindgen = "0.71" cc = "1.2.0" -duct = { version = "1", optional = true } dunce = "1.0.5" -flate2 = { version = "1.0.28", optional = true } regex = "1.11.1" -tar = { version = "0.4.40", optional = true } -ureq = { version = "3.0.10", optional = true } -which = { version = "7.0.0", optional = true } +nginx-src = { version = "1028.0.0", optional = true, path = "../nginx-src" } [features] -vendored = ["dep:which", "dep:duct", "dep:ureq", "dep:flate2", "dep:tar"] +vendored = ["dep:nginx-src"] diff --git a/nginx-sys/build/main.rs b/nginx-sys/build/main.rs index 6028f31..d7e526b 100644 --- a/nginx-sys/build/main.rs +++ b/nginx-sys/build/main.rs @@ -6,9 +6,6 @@ use std::fs::{read_to_string, File}; use std::io::Write; use std::path::{Path, PathBuf}; -#[cfg(feature = "vendored")] -mod vendored; - const ENV_VARS_TRIGGERING_RECOMPILE: &[&str] = &["OUT_DIR", "NGINX_BUILD_DIR", "NGINX_SOURCE_DIR"]; /// The feature flags set by the nginx configuration script. @@ -130,8 +127,9 @@ impl NginxSource { #[cfg(feature = "vendored")] pub fn from_vendored() -> Self { - let build_dir = vendored::build().expect("vendored build"); - let source_dir = build_dir.parent().expect("source directory").to_path_buf(); + let out_dir = env::var("OUT_DIR").unwrap(); + let build_dir = PathBuf::from(out_dir).join("objs"); + let (source_dir, build_dir) = nginx_src::build(build_dir).expect("nginx-src build"); Self { source_dir, @@ -202,7 +200,7 @@ fn generate_binding(nginx: &NginxSource) { .map(|path| format!("-I{}", path.to_string_lossy())) .collect(); - print_cargo_metadata(&includes).expect("cargo dependency metadata"); + print_cargo_metadata(nginx, &includes).expect("cargo dependency metadata"); // bindgen targets the latest known stable by default let rust_target: bindgen::RustTarget = env::var("CARGO_PKG_RUST_VERSION") @@ -287,7 +285,10 @@ fn parse_includes_from_makefile(nginx_autoconf_makefile_path: &PathBuf) -> Vec

>(includes: &[T]) -> Result<(), Box> { +pub fn print_cargo_metadata>( + nginx: &NginxSource, + includes: &[T], +) -> Result<(), Box> { // Unquote and merge C string constants let unquote_re = regex::Regex::new(r#""(.*?[^\\])"\s*"#).unwrap(); let unquote = |data: &str| -> String { @@ -327,6 +328,11 @@ pub fn print_cargo_metadata>(includes: &[T]) -> Result<(), Box Option { @@ -22,42 +24,68 @@ pub fn cstr_to_path(val: &std::ffi::CStr) -> Option { Some(PathBuf::from(str)) } +/// Find nginx binary in the build directory +pub fn find_nginx_binary() -> io::Result { + let path = [ + // TEST_NGINX_BINARY is specified for tests + env::var("TEST_NGINX_BINARY").ok().map(PathBuf::from), + // The module is built against an external NGINX source tree + env::var("NGINX_BUILD_DIR") + .map(PathBuf::from) + .map(|x| x.join(NGINX_BINARY_NAME)) + .ok(), + env::var("NGINX_SOURCE_DIR") + .map(PathBuf::from) + .map(|x| x.join("objs").join(NGINX_BINARY_NAME)) + .ok(), + // Fallback to the build directory exposed by nginx-sys + option_env!("DEP_NGINX_BUILD_DIR") + .map(PathBuf::from) + .map(|x| x.join(NGINX_BINARY_NAME)), + ] + .into_iter() + .flatten() + .find(|x| x.is_file()) + .ok_or(io::ErrorKind::NotFound)?; + + Ok(path) +} + /// harness to test nginx pub struct Nginx { - pub install_path: PathBuf, + pub prefix: tempfile::TempDir, + pub bin_path: PathBuf, pub config_path: PathBuf, } impl Default for Nginx { /// create nginx with default fn default() -> Nginx { - let install_path = cstr_to_path(NGX_PREFIX).expect("installation prefix"); - Nginx::new(install_path) + let binary = find_nginx_binary().expect("nginx binary"); + Nginx::new(binary).expect("test harness") } } impl Nginx { - pub fn new>(path: P) -> Nginx { - let install_path = path.as_ref(); - let config_path = cstr_to_path(NGX_CONF_PATH).expect("configuration path"); - let config_path = install_path.join(config_path); - - Nginx { - install_path: install_path.into(), - config_path, - } - } + pub fn new(binary: impl AsRef) -> io::Result { + let prefix = tempfile::tempdir()?; + let config = prefix.path().join("nginx.conf"); + + fs::create_dir(prefix.path().join("logs"))?; - /// get bin path to nginx instance - pub fn bin_path(&mut self) -> PathBuf { - let bin_path = cstr_to_path(NGX_SBIN_PATH).expect("binary path"); - self.install_path.join(bin_path) + Ok(Nginx { + prefix, + bin_path: binary.as_ref().to_owned(), + config_path: config, + }) } /// start nginx process with arguments - pub fn cmd(&mut self, args: &[&str]) -> Result { - let bin_path = self.bin_path(); - let result = Command::new(bin_path).args(args).output(); + pub fn cmd(&self, args: &[&str]) -> Result { + let prefix = self.prefix.path().to_string_lossy(); + let config_path = self.config_path.to_string_lossy(); + let args = [&["-p", &prefix, "-c", &config_path], args].concat(); + let result = Command::new(&self.bin_path).args(args).output(); match result { Err(e) => Err(e), diff --git a/tests/nginx.conf b/tests/nginx.conf index 495fc07..1d2e207 100644 --- a/tests/nginx.conf +++ b/tests/nginx.conf @@ -16,7 +16,7 @@ events { http { - include mime.types; + # include mime.types; default_type application/octet-stream;